按秩合并并查集
按秩合并的并查集
1.通过比较并查集的大小来连边,将小的(u)向大的(v)连边,这样对于大的并查集,查询代价不变,而小的并查集查询代价每个点增加了1,相当于增加了siz[u],(反过来则变成了siz[v]),siz[u]<siz[v]所以这样更优,树高不会超过\(logn\),因为每上一层大小减半
2.相较于路径压缩的并查集,他可以保留原始的信息
例题:[bzoj4668]冷战
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cctype>
using namespace std;
const int MAXX=500010;
int f[MAXX],siz[MAXX],edge[MAXX],deep[MAXX];
int n,m,tim,last;
inline int find(int x){
if(f[x]==x)return x;
else {
int ff=find(f[x]);
deep[x]=deep[f[x]]+1;
return ff;
}
}
inline int read(){
int x=0;bool f=0;
char c=getchar();
while(!isdigit(c)){
if(c=='-')f=1;
c=getchar();
}
while(isdigit(c)){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return f?-x:x;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i){
f[i]=i;
siz[i]=1;
}
for(int i=1;i<=m;++i){
int opt,a,b;
opt=read();a=read();b=read();
a^=last;b^=last;
if(!opt){
tim++;
int fu=find(a);int fv=find(b);
if(fu==fv)continue;
if(siz[fu]>siz[fv])swap(fu,fv);//按秩合并,启发式合并
f[fu]=fv;
edge[fu]=tim;//记录边权信息
siz[fv]+=siz[fu];
}else {
int x=a;int y=b;
int ans=0;
if(find(x)!=find(y)){
last=0;
printf("0\n");
continue;
}
while(x!=y){
if(deep[x]<deep[y])swap(x,y);
ans=max(ans,edge[x]);
x=f[x];
}//相当于寻找lca
last=ans;
printf("%d\n",ans);
}
}
return 0;
}