题解:P12506 「ROI 2025 Day2」沼泽里的青蛙

题目描述

在索契筹备 2014 年奥运会时,意外引入了来自远东的小型蝴蝶——箱树蛾。它毁掉了当地的箱树林,迫使树蛙们搬到了沼泽里生活。不过,这些聪明的树蛙依然保留了它们的神奇本领:每次跳跃后,它们的颜色会在绿色和棕色之间切换。

沼泽是一片平面,上面散布着许多小土丘,可以看作平面上的点。青蛙一次跳跃可以从当前土丘跳到任何一个距离不超过 \(r\) 的其他土丘。跳跃后,青蛙的颜色会变成相反的颜色。需要注意的是,青蛙无法在原地跳跃。

你的任务是,对于从 \(1\)\(n\) 的每个起始土丘,判断青蛙能否通过若干次跳跃回到这个土丘,并且在回到时颜色与初始时相反。

思路

考虑暴力,容易想到用扩展域并查集。并查集的前 \(1\)\(n\) 表示绿色,后 \(n+1\)\(2\times n\) 表示棕色。如果 \(i\)\(j\) 连个点之间有连边,就把 \(f_i\)\(f_{j+n}\) 放到同一个并查集里,把 \(f_{i+n}\)\(f_j\) 放到同一个里面,最后只需判断 \(f_i\)\(f_{i+n }\) 是否在同一个并查集里即可。

现在考虑如何优化,想到分块,以 \(\frac{r}{2}\) 的长度分块,这样可以保证块内的每一个点都可以互相到达。注意到如果一个块内有大于等于 \(3\) 个点,那么这个块内的每一个点都可以完成任务,我们称这样一个块是好的。同理一个坏的块即为块内只有小于等于 \(2\) 个点。考虑块和块之间的转移,注意到因为是按 \(\frac{r}{2}\) 的长度分的块,所以每次转移只需要和周围两层的块转移,不包括四个角上的块。如果是两个好的块就没必要转移,如果是一个好块和一个坏块,或者是两个坏块就暴力连边,用扩展域并查集暴力。那么这道题就做完了,复杂度是 \(O(n\log(n))\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=510101;
const int M=1e9+7;
ll n,r,x[N],y[N],tot,base[N],vis[N],fa[N],R;
vector<ll>g[N];
map<ll,ll>mp;
ll js(ll x,ll y){return x*M+y;}
ll id(ll x,ll y){
    ll d=js(x,y);
    if(mp.count(d))return mp[d];
    return mp[d]=++tot;
}
ll find(ll x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void merge(ll x,ll y){
    fa[find(x)]=find(y+n);
    fa[find(x+n)]=find(y);
}
bool check(ll a,ll b){return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])<=R*R;}
int main(){
	freopen("frog.in","r",stdin);
	freopen("frog.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>r,R=r;
    r=max(int(r*1.0*0.7),1);
    for(int i=1;i<=(n<<1);i++)fa[i]=i;
	for(int i=1;i<=n;i++){
        cin>>x[i]>>y[i];
        base[i]=id(x[i]/r,y[i]/r);
        g[base[i]].push_back(i);
    }
    for(int i=1;i<=tot;i++){
        if(g[i].size()>=3)vis[i]=1;
        else if(g[i].size()==2)merge(g[i][0],g[i][1]);
    }
    for(int i=1;i<=n;i++){
        if(vis[base[i]])merge(i,i);
        ll xx=x[i]/r,yy=y[i]/r;
        for(int dx=-2;dx<=2;dx++){
            for(int dy=-2;dy<=2;dy++){
                if(abs(dx)+abs(dy)==4||abs(dx)+abs(dy)==0)continue;
                ll kx=xx+dx,ky=yy+dy;
                ll d=js(kx,ky);
                if(!mp.count(d))continue;
                d=mp[d];
                if(vis[base[i]]&&vis[d])continue;
                for(auto j:g[d]){
                    if(check(i,j))merge(i,j);
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(find(i)==find(i+n))cout<<1;
        else cout<<0;
    }
	return 0;
}
posted @ 2025-09-10 21:38  一班的hoko  阅读(12)  评论(0)    收藏  举报