并查集(Disjoint Set Union,DSU)

定义:

并查集是一种用来管理元素分组情况的数据结构。

作用:

查询元素a和元素b是否属于同一组

合并元素a和元素b所在的组

优化方法:

1.路径压缩

2.添加高度属性

拓展延伸:

分组并查集

带权并查集

代码如下:

//带有路径压缩的并查集
//一句话并查集(常用)
int dsu(int x){
    return x==par[x]?x:(par[x]=dsu(par[x]));
}
//带有路径压缩和高度的并查集
//ranks[i]代表以i为根的树的最大高度,若不存在则为0
int rank[maxn];
int par[maxn];
void init(int n){
    for(int i=0;i<n;i++){
        par[i]=i;
        rank[i]=0;
    }
}
int dsu(int x){
    if(par[x]==x){
        return x;
    }else{
        return par[x]=dsu(par[x]);
    }
}
void union(int x,int y){
    int fx=dsu(x);
    int fy=dsu(y);
    if(fx==fy){
        return;
    }
    if(rank[fx]<rank[fy]){
        par[fx]=fy;
    }else if(rank[fx]>rank[fy]){
        par[fy]=fx;
    }else{
        par[fy]=fx;
        rank[fx]++;
    }
}
bool same(int x,int y){
    return dsu(x)==dsu(y);
}
poj1182 食物链
//带权并查集解法
/*
模仿矢量计算来处理权值
当我们知道x与祖先x1的关系,y与祖先y1的关系,x与y的关系时,求x1与y1的关系时,使用矢量 
计算: 
x1->x ->y ->y1
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50010;
int par[maxn],ran[maxn];
int dsu(int x){
    if(x==par[x]){
        return x;
    }else{
        int fx=dsu(par[x]);
        //一开始将par[x]写成了fx,一直wa
        //如果是fx,那么每一次都到最终的根节点,求解ran数组显然不正确
        ran[x]=(ran[x]+ran[par[x]]+3)%3;
        par[x]=fx;
        return par[x];
    }
}
int main()
{
    int n,m;
    int ty,x,y,ans=0;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++){
        par[i]=i;
        ran[i]=0;
    }
    while(m--){
        scanf("%d%d%d",&ty,&x,&y);
        if(x==y&&ty==2){
            ans++;
        }
        else if(x>n||y>n){
            ans++;
        }
        else{
            int fx=dsu(x);
            int fy=dsu(y);
            if(fx==fy)
            {
                if((ran[x]-ran[y]+3)%3!=ty-1){
                    ans++;
                    //这里如果不相等,说明这句话是错误的,continue
                    //一开始因为没写continue,一直wa
                    continue;
                }
            }
            par[fx]=fy;
            ran[fx]=(-ran[x]+ty-1+ran[y]+3)%3;
        }
    }
    printf("%d\n",ans);
    return 0;
}
//分组并查集解法
/*
个人理解带权并查集就是多个并查集,
然后一个set所代表的的关系不仅一种,
所以对每个元素要用多重表示,在这个题目里
就是+i*n来区分每个元素和另一些元素的不同关系
*/
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const int maxn = 200000+10;
int par[maxn];

int dsu(int x){
    return x==par[x]?x:(par[x]=dsu(par[x]));
}
bool same(int x,int y){
    return dsu(x)==dsu(y);
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=3*n;i++){
        par[i]=i;
    }
    int cnt=0;
    int x,y,ty;
    while(m--){
        scanf("%d%d%d",&ty,&x,&y);
        if(x<=0||x>n||y<=0||y>n){
            cnt++;
            continue;
        }
        if(ty==1){
            if(same(x,y+n)||same(y,x+n)){
                cnt++;
            }else{
                for(int i=0;i<3;i++){
                    int fx=dsu(x+i*n);
                    int fy=dsu(y+i*n);
                    par[fx]=fy;
                }
            }
        }else{
            if(x==y||same(x,y)||same(y,x+n)){
                cnt++;
            }else{
                for(int i=0;i<3;i++){
                    int fx=dsu(x+i*n);
                    int fy=dsu(y+(i+1)%3*n);
                    par[fx]=fy;
                }
            }
        }
    }
    printf("%d\n",cnt);
    return 0;
}

  

 

  

posted @ 2018-03-25 19:28  愿~得偿所愿,不负时光  阅读(357)  评论(0编辑  收藏  举报