并查集模板

并查集

作用:
1.将两个集合合并
2.询问两个元素是否在一个集合当中

暴力做法:O(n)

belong[x]=x
1.把belong[x]=a改为b O(n)
2.belong[x1]==belong[x2] O(1)

而用并查集复杂度:近乎O(1)

每个集合以树的形式维护’
每个集合编号是它根结点的标号
每个点都存储其父结点标号,就能通过其父结点编号快速找到该结点是属于哪个集合的
p[x]表示x的父结点

问题1:
如何判断某一结点是不是根结点

if(p[x]]==x)

问题2:
如何求x的集合编号
向上找其根结点

while(p[x]!=x){
   x=p[x];
}

问题3:
如何合并两个结合·
将一个集合的根结点作为另一个集合的根结点的儿子
px为集合x的编号,py是集合y的编号
合并,令

p[x]=y;

优化:(路径压缩)
找到根节点后,让路径上所有点都直接指向根结点,从而进行路径压缩,减少搜索的长路

注意:scanf读字符容易读进空格和回车,scanf读字符串,自动忽略空格和回车
要用scanf读入一个字符建议用字符串的形式

例题:合并集合

在这里插入图片描述

#include <iostream>
using namespace std;
const int N=100100;
int p[N];
int find(int x){
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++)p[i]=i;
    
    for(int i=0;i<m;i++){
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        
        if(op[0]=='M'){
            p[find(a)]=find(b);
        }else {
            
            if(find(a)==find(b)){
                puts("Yes");
            }else{
                puts("No");
            }
        }
    }
    return 0;
}

连通块中点的数量

并查集当前集合中点的个数–维护额外变量

可能存在自环和重边

只保证根结点的size有意义
在这里插入图片描述

#include <iostream>
using namespace std;
const int N=100100;
int p[N],s[N];

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

int main(){
    
    int n,m;
    
    scanf("%d%d",&n,&m);
   
    for(int i=1;i<=n;i++){
        p[i]=i;
        s[i]=1;
    }
    
    for(int i=0;i<m;i++){
        
        char op[5];
        int a,b;
        
        scanf("%s",op);
        
        if(op[0]=='C'){
            
            
           scanf("%d%d",&a,&b);
           
           if(find(a)==find(b))continue;
           s[find(a)]+=s[find(b)];
           p[find(b)]=find(a);
          
           
           
        }else if(op[1]=='1'){
            
            scanf("%d%d",&a,&b);
            
            if(find(a)==find(b))puts("Yes");
            else puts("No");
            
            
        }else{
            
            scanf("%d",&a);
            
            printf("%d\n",s[find(a)]) ;
        }
    }
    
    
    
    return 0;
}

食物链

在这里插入图片描述

#include <iostream>
using namespace std;
const int N=5e4+10,M=3;
int p[N]; 
int cnt;
int d[N];//当前节点到父节点的距离
int find(int x){
   if(p[x]!=x){
     int pre=p[x];
     p[x]=find(p[x]);
     d[x]=(d[x]+d[pre])%M;
   }
   return p[x];
}
void D(int a,int b,int n){ 
    int p1=find(a);
    int p2=find(b);
    if(p1==p2){
        if(d[a]%M!=(d[b]+n)%M)cnt++;
        return;
    }
    p[p2]=p1;
    d[p2]=((d[a]-d[b]-n)+M)%M;
}
int main(){
    int n,m;
   
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++){
        p[i]=i;
        d[i]=0;
    }
    
    while(m--){
        int op,a,b;
        scanf("%d%d%d",&op,&a,&b);
        if(a>n||b>n){
            cnt++;
            continue;
        }
        D(a,b,op-1);
    } 
         
        

    printf("%d",cnt);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值