并查集专题讲解

一,并查集的用途

*并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
例如:1,合并两个集合,复杂度是O(1)。
2,查询一个元素在哪个集合里面,复杂度O(1)。
3,查询两个元素是否在同一个集合里面。


二,并查集建立的主要步骤:

1,初始化:每个结点的父亲结点首先设为它本身。

void init(){
    for(int i=1;i<=n;i++)fa[i]=i,total[i]=1;
    //注意有需要时才加total[i]数组,total数组仅是用来计数的。 
}

2,路径压缩(解决特殊情况下的树的层次深而造成的复杂度增大的问题)

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}

3,合并

void Merge(int a,int b){
    int p1=find(a),p2=find(b);
    if(p1!=p2)fa[p2]=p1;
    //当需要计数时代码需改为:if(p1!=p2)fa[p2]=p1,total[p1]+=total[p2];
}

三,并查集注意事项

1,前期未经过路径压缩的树形结构并查集,树的深度有可能是n。
未经过路径压缩的并查集find()函数(获得父亲结点的):

int find(int x){return fa[x]==x?x:find(fa[x]);}

例如:这里写图片描述
2,并查集一定要记得初始化。
3,有些并查集的题目用cin,cout有可能会超时。遇到常数比较大的尽量不要用cin,cout输入输出。


四,例题:

1.*看题目请戳这里poj 1611 The Suspects此题是一个模板题
AC code:

#include<iostream>
using namespace std;
const int N=3e4+6;
int n,m,k,fa[N],total[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
    int p1=find(a),p2=find(b);
    if(p1!=p2)total[p1]+=total[p2],fa[p2]=p1;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    while(cin>>n>>m,n||m){
        for(int i=0;i<n;i++)fa[i]=i,total[i]=1;
        for(int i=0;i<m;i++){
            int h,s;cin>>k>>h;
            for(int j=1;j<k;j++){
                cin>>s;Merge(h,s);
            }
        }
        cout<<total[find(0)]<<endl;
    }
}

2,看题请戳这里hdu1232
AC code:

#include<iostream>
using namespace std;
const int N=1e3+6;
int n,m,ans,fa[N];
int find(int x){return fa[x]==-1?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
    int p1=find(a),p2=find(b);
    if(p1!=p2)fa[p1]=p2;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    while(cin>>n>>m,n){ans=0;
        for(int i=1;i<=n;i++)fa[i]=-1;
        for(int i=1;i<=m;i++){
            int a,b;cin>>a>>b;
            Merge(a,b);
        }
        for(int i=1;i<=n;i++)
            if(fa[i]==-1)ans++;
        cout<<ans-1<<endl;
    }
}

3,模板微变形。poj 1988 堆箱子
题意:给你P个操作,有两种可能,一是移动操作,移动操作就是把X砖的这一堆放到Y砖这一堆的上面。其次是统计操作,统计X砖下面一共有多少砖。
堆箱子
AC code:

#include<iostream>
using namespace std;
const int N=1e5+6;
int fa[N],under[N],sum[N];
int find(int x){
    if(fa[x]==x)return x;
    int t=find(fa[x]);
    under[x]+=under[fa[x]];
    fa[x]=t;return fa[x];
}
void Merge(int a,int b){
    int p1=find(a),p2=find(b);
    if(p1!=p2)fa[p1]=p2,under[p1]=sum[p2],sum[p2]+=sum[p1];
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int T;cin>>T;
    for(int i=1;i<=N-1;i++){fa[i]=i;under[i]=0;sum[i]=1;}
    while(T--){
        char ch;int a,b,c;cin>>ch;
        if(ch=='M'){cin>>a>>b;Merge(a,b);}
        else {cin>>c;find(c);cout<<under[c]<<endl;}
    }
}

4,并查集的拓展应用:poj 1182 食物链
*题意:
这里写图片描述
输入输出:
这里写图片描述
思路:

AC code:

#include<cstdio>
const int N=15e5+6;
int n,k,T[N],X[N],Y[N],fa[N],ans;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void Merge(int a,int b){
    int p1=find(a),p2=find(b);
    if(p1!=p2)fa[p2]=p1;
}
void solve(){
    for(int i=0;i<=n*3;i++)fa[i]=i;
    for(int i=0;i<k;i++){
        int t=T[i];int x=X[i]-1,y=Y[i]-1;
        if(x<0||n<=x||y<0||n<=y){ans++;continue;}
        if(t==1){
            if(find(x)==find(y+n)||find(x)==find(y+2*n))ans++;
            else {
                Merge(x,y);
                Merge(x+n,y+n);
                Merge(x+2*n,y+2*n);
            }
        }
        else 
             if(find(x)==find(y)||find(x)==find(y+2*n))ans++;
             else {
                Merge(x,y+n);
                Merge(x+n,y+2*n);
                Merge(x+2*n,y);
             }
    }
    printf("%d\n",ans);
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=0;i<k;i++)scanf("%d%d%d",T+i,X+i,Y+i);
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值