并查集的操作有三步,初始化,查找祖先与合并。
既然并查集是来查找祖先的,那么初始化就必然是让每个点的祖先指向自己
for(int i=1;i<=n;++i) fa[i]=i;
查找操作就是不断地向上走,直到找到祖先为止
while(x!=fa[x]) x=fa[x];
合并操作就是把一个节点的祖先变为另一个节点的祖先。
fa[find(a)]=find(b);//其中find(x)为x的祖先
我们需对路径进行压缩。
即当我们经过找到祖先节点后,回溯的时候顺便将它的子孙节点都直接指向祖先,使以后的查找复杂度变回O(logn)甚至更低
while(x!=fa[x]) x=fa[x]=fa[fa[x]];
完整代码:
#include<cstdio> #include<iostream> #include<cstdlib> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<time.h> #include<queue> using namespace std; typedef long long ll; typedef long double ld; typedef pair<int,int> pr; const double pi=acos(-1); #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define Rep(i,u) for(int i=head[u];i;i=Next[i]) #define clr(a) memset(a,0,sizeof a) #define pb push_back #define mp make_pair #define fi first #define sc second ld eps=1e-9; ll pp=1000000007; ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} ll read(){ ll ans=0; char last=' ',ch=getchar(); while(ch<'0' || ch>'9')last=ch,ch=getchar(); while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); if(last=='-')ans=-ans; return ans; }//head int n,m,p,x,y,z; int fa[20005]; int find_die(int x)//找爹函数 { while(fa[x]!=x) x=fa[x]=fa[fa[x]]; //将它的子孙节点都直接指向祖先 return x; } void uni(int r,int k)//合并函数 { fa[k]=r; } inline void print(int x,int y)//输出函数 { if(find_die(x)==find_die(y)) cout<<"Y"<<endl; else cout<<"N"<<endl; } int main() { n=read(),m=read(); rep(i,1,n) fa[i]=i; rep(i,1,m) { z=read(),x=read(),y=read(); if(z==1) { int r1=find_die(x); int r2=find_die(y); if(r1!=r2) uni(r1,r2); } else if(z==2) print(x,y); } return 0; }