按秩合并并查集

按秩合并的并查集

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
");
                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
",ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ARTlover/p/9752355.html