题目:CF755D
洛谷
Codeforces
这是一道数据结构大水题。
打模拟赛的时候把这道题放到了 T4,做完 T1 就看这道题有思路就写出来了。
首先考虑如何暴力模拟计算答案,当每链接一条边 $(x, x + k)$ 时,被分割的区域数 $s$ 会增加 $u + 1$,其中 $u$ 是与这条边在正多边形中有交叉的线段数量。而题目保证边数(即点数)$n$ 与 $k$ 互质,说明在每一个点都被连接后才会回到初始节点,共链接 $n$ 条边。我们把每一次的连边存下来,在新的一次连边时判断有多少条边与此时的连边相交。暴力复杂度 $O(n^2)$,期望得分 $0$ 分。
考虑优化。时间复杂度的瓶颈是计算重复的边。想出了一个很奇怪的判断方法,用两颗树状数组储存每条边的左节点和右节点,由于 $k$ 是确认的,所以只用一个节点的位置就可以求出另一个节点。对于左节点与当前边 $(l, r)$ 相交时,统计左节点在区间 $[r - k + 1, r - 1]$ 的线段数量,右节点则统计 $[l + 1, l + k - 1]$。如果节点越界的话特判一下 $\pm n$ 即可。使用树状数组的话复杂度 $O(n \log n)$。
最开始要注意把 $k$ 赋值为 $\min(k, n - k)$,这两种情况是等效的,但是可以缩小一点范围防止越界。卡这个卡了很久。
懒得修的赛事代码。凑合看看就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #include <iostream> #include <cstring> #include <cstdio> #include <queue> #define int long long using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch - 48), ch = getchar(); return x * f; } const int N = 2e6 + 10;
typedef pair<int, int> pii; int n, k; priority_queue <int> E[N]; vector <pii> V; namespace Tree { int vall[N], valr[N]; int lowbit(int x) { return x & -x; } int queryl(int x) { int ans = 0; while(x) { ans += vall[x]; x -= lowbit(x); } return ans; } void modifyl(int x, int y) { while(x <= n) { vall[x] += y; x += lowbit(x); } } int queryr(int x) { int ans = 0; while(x) { ans += valr[x]; x -= lowbit(x); } return ans; } void modifyr(int x, int y) { while(x <= n) { valr[x] += y; x += lowbit(x); } } } void solve(); signed main() { int T; T = 1; while(T--) solve(); return 0; } using namespace Tree; int calc1(int l, int r) { if(l >= 1 && r <= n) return queryl(r) - queryl(l - 1); else return queryl(n) - queryl(l - 1 + n) + queryl(r); } int calc2(int l, int r) { if(l >= 1 && r <= n) return queryr(r) - queryr(l - 1); else return queryr(n) - queryr(l - 1) + queryr(r - n); } int SegmentJudge(int l, int r) { int ans = 0; if(l > r) swap(l, r); ans += calc1(r - k + 1, r - 1); ans += calc2(l + 1, l + k - 1); return ans; } void solve() { n = read(), k = read(); k = min(k, n - k); int x = 1; int ans = 1; for(int i = 1; i <= n; i++) { int y = (x + k > n ? x + k - n: x + k); ans += SegmentJudge(x, y) + 1; cout << ans << " "; Tree::modifyl(x, 1); Tree::modifyr(y, 1); x = y; } cout << endl; return; }
|