[题解]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;
}
posted @ 2026-02-26 15:12  WBIKPS  阅读(0)  评论(0)    收藏  举报