1 条题解
-
0
思路
先从 subtask 9 入手,这个 sub 满足任意两点之间的距离不小于 。感性理解一下,发现这种情况的边数是 量级的。
考虑如何快速找到这些有效的边。想办法找到 的作用,将整个平面按 个单位长度分块,每一块为一个 的正方形区域。分块过后有一个很好的性质,每块内只有 个点,且这些点两两均有连边,同时每块内的点都只有可能与以其为中心的 个块有连边。判断是否能以相反颜色跳回使用拓展域并查集即可。
接下来思考满分做法。继续沿用上面的分块方法,注意到若一个块内存在 个点,那么这些点显然可以只通过走块内的点获得一个可行的路径,所以对于这种块内的点的答案一定是
1,在并查集上可以直接连接 。对于只存在 个点的块,采用 subtask 9 的方法暴力连边即可。这时候可以大胆猜测这样做复杂度就是 的,证明也比较容易。为了方便叙述,我们称 个点的块为大块, 个点的块为小块。那么对于每一个小块和大块都最多与 个小块连边,所以显然连边的量级是 的(大约会有 的常数)。
Code
#include <bits/stdc++.h> #define re register #define fst first #define snd second #define int long long using namespace std; typedef pair<int,int> pii; const int N = 2e5 + 10; int n,r; int fp[N]; pii arr[N]; map<pii,vector<int>> mp; inline int read(){ int r = 0,w = 1; char c = getchar(); while (c < '0' || c > '9'){ if (c == '-') w = -1; c = getchar(); } while (c >= '0' && c <= '9'){ r = (r << 3) + (r << 1) + (c ^ 48); c = getchar(); } return r * w; } inline int find(int x){ if (fp[x] == x) return fp[x]; else return fp[x] = find(fp[x]); } inline void merge(int a,int b){ int x = find(a),y = find(b); if (x != y) fp[x] = y; } signed main(){ n = read(),r = read(); const int B = (r + 1) / 2; for (re int i = 1;i <= n * 2;i++) fp[i] = i; for (re int i = 1;i <= n;i++){ arr[i].fst = read(),arr[i].snd = read(); mp[{arr[i].fst / B,arr[i].snd / B}].push_back(i); } for (auto it = mp.begin();it != mp.end();it++){ vector<int> v = (it -> snd); if (v.size() >= 3){ for (int x:v) merge(x,x + n); } else{ for (re int i = (it -> fst).fst - 2;i <= (it -> fst).fst + 2;i++){ for (re int j = (it -> fst).snd - 2;j <= (it -> fst).snd + 2;j++){ if (mp.count({i,j})){ for (int x:v){ for (int y:mp[{i,j}]){ if (x != y && (arr[x].fst - arr[y].fst) * (arr[x].fst - arr[y].fst) + (arr[x].snd - arr[y].snd) * (arr[x].snd - arr[y].snd) <= r * r){ merge(x,y + n); merge(x + n,y); } } } } } } } } for (re int i = 1;i <= n;i++) printf("%d",(find(i) == find(i + n)) ? 1 : 0); return 0; }
- 1
信息
- ID
- 9584
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者