[题解]P12506 「ROI 2025 Day2」沼泽里的青蛙
思路
先从 subtask 9 入手,这个 sub 满足任意两点之间的距离不小于 \(\frac{r}{2}\)。感性理解一下,发现这种情况的边数是 \(\Theta(n)\) 量级的。
考虑如何快速找到这些有效的边。想办法找到 \(\frac{r}{2}\) 的作用,将整个平面按 \(\frac{r}{2}\) 个单位长度分块,每一块为一个 \(\frac{r}{2} \times \frac{r}{2}\) 的正方形区域。分块过后有一个很好的性质,每块内只有 \(\Theta(1)\) 个点,且这些点两两均有连边,同时每块内的点都只有可能与以其为中心的 \(5 \times 5\) 个块有连边。判断是否能以相反颜色跳回使用拓展域并查集即可。
接下来思考满分做法。继续沿用上面的分块方法,注意到若一个块内存在 \(\geq 3\) 个点,那么这些点显然可以只通过走块内的点获得一个可行的路径,所以对于这种块内的点的答案一定是 1,在并查集上可以直接连接 \((i,\neg i)\)。对于只存在 \(< 3\) 个点的块,采用 subtask 9 的方法暴力连边即可。
这时候可以大胆猜测这样做复杂度就是 \(\Theta(n)\) 的,证明也比较容易。为了方便叙述,我们称 \(\geq 3\) 个点的块为大块,\(< 3\) 个点的块为小块。那么对于每一个小块和大块都最多与 \(5 \times 5\) 个小块连边,所以显然连边的量级是 \(\Theta(n)\) 的(大约会有 \(50\) 的常数)。
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;
}

浙公网安备 33010602011771号