并查集

作用:

  并查集顾名思义就是有“合并集合”和“查找集合中的元素”两种操作的关于数据结构。

代表元思想:

  用集合中的某个元素来代表这个集合,该元素称为集合的代表元
组织结构:

  一个集合内的所有元素组织成以代表元为根的树形结构。也就是是说同一个集合中的元素在同一棵树里。

合并两个集合:

  将一棵树作为另一棵树的子树。也就是将代表元作为别人的子节点。

查找集合中的两个元素:

  讨论他们代表元是否一样。

一些题目:

POJ1703  Find them, Catch them

题意:

有两个帮派,

操作D:表示 a,b不属于一个帮派 。操作A:询问a,b是否为同一个帮派。

思路:

同一个代表元中的东西表示他们属于同一集合。我们可以很容易表示a,b属于同一个集合,那么如何表示属于不同的集合。

我们可以用a+N表示a的对立面,如果a+N和b在同一集合内,那么表示a,b不在同一个集合内。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 const int MAXN=1e5+7;
 4 int par[MAXN*2];
 5 int find(int x){
 6     return par[x]==x?par[x]:par[x]=find(par[x]);
 7 }
 8 void unit(int a,int b){
 9     a=find(a);
10     b=find(b);
11     if(a!=b){
12         par[a]=b;
13     }
14 }
15 int main(){
16     int T;scanf("%d",&T);
17     while(T--){
18         int N,M;scanf("%d%d",&N,&M);getchar();
19         for(int i=1;i<=N*2;++i){
20             par[i]=i;
21         }
22         for(int i=0;i<M;++i){
23             char ch=getchar();int a,b;
24             scanf("%d%d",&a,&b);getchar();
25             if(ch=='D'){
26                 unit(a,b+N);
27                 unit(a+N,b);
28             }else{
29                 int x1=find(a),x2=find(a+N);
30                 int y1=find(b),y2=find(b+N);
31                 if(x1==y1||x2==y2)printf("In the same gang.
");
32                 else if(x1==y2||x2==y1)printf("In different gangs.
");
33                 else printf("Not sure yet.
");
34             }
35         }
36     }
37     return 0;
38 }
poj1703

 也可以用带权并查集做:

这里有篇讲得不错的博客:http://blog.csdn.net/mengxiang000000/article/details/51142345

大概思想就是通过这样的思想:

敌人的敌人是朋友。

通过计算两个节点间的距离来确定这两个点是不是同一个集合里的。

代码:

#include<cstdio>
#include<cstring>
const int MAXN=1e5+7;
int par[MAXN],sum[MAXN];
int find(int x){
    if(par[x]==x)return x;
    int top=find(par[x]);
    sum[x]+=sum[par[x]];
    return par[x]=top;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        int N,Q;scanf("%d%d",&N,&Q);getchar();
        for(int i=1;i<=N;++i)par[i]=i,sum[i]=0;
        for(int i=0;i<Q;++i){
            char ch;int x,y;
            scanf("%c%d%d",&ch,&x,&y);getchar();
            if(ch=='D'){
                int xx=find(x);
                int yy=find(y);
                if(xx==yy)continue;
                par[xx]=yy;
                //printf("par[%d]=%d
",xx,par[xx]);
                sum[xx]=sum[y]+sum[x]+1;
                //printf("sum[%d]=%d
",xx,sum[xx]);
            }else{
                int xx=find(x);
                int yy=find(y);
                if(yy==xx){
                    if((sum[x]-sum[y])%2==0){
                        printf("In the same gang.
");
                    }else{
                        printf("In different gangs.
");
                    }
                }else{
                    if(N==2)printf("In different gangs.
");
                    else printf("Not sure yet.
");
                }
            }
        }
    }
    return 0;
}
poj1703带权并查集

 ural 1003 parity

题意:

    给你一些信息l,r,odd/even 代表从l到r的和为奇数或者偶数。问从哪一条开始错了。

思路:

   考虑到数据范围很大,但N不大,可以考虑离散化。然后sum[l...r]=sum[r]-sum[l-1],也就是转化为前缀和。如果是偶数那么r和l-1在同一个集合里,否则就不是。我们认为在一个集合里的奇偶性是一样的。需要注意的是需要先判断是否符合条件,再进行集合的合并。顺序千万不要弄错!因为先进行合并会改变他们的关系。

   另外,对在不在一个集合里,奇数偶数这种转化为并查集思想一定要敏感。

代码:

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
map<int,int>m;
const int MAXN=1e4+7;
int par[MAXN*2];
int tot;
int get(int x){
    if(!m[x])m[x]=tot++;
    return m[x];
}
int find(int x){
    return par[x]==x?par[x]:par[x]=find(par[x]);
}
void unit(int x,int y){
    //printf("unit:x=%d,y=%d
",x,y);
    x=find(x);
    y=find(y);
    if(x!=y){
        par[x]=y;
    }
}
int main(){
    int T;
    while(scanf("%d",&T)&&T!=-1){
        int N;scanf("%d",&N);
        for(int i=1;i<=N*2*2;++i)par[i]=i;
        m.clear();
        tot=1;
        int ans=N;
        for(int k=1;k<=N;++k){
            char s[10];
            int x,y;scanf("%d%d%s",&x,&y,s);
            if(ans!=N)continue;
            x--;
            x=get(x);y=get(y);
            if(s[0]=='e'){
                int xx=find(x),yy=find(y);
                if(find(x)==find(y+2*N)||find(x+2*N)==find(y)){
                    ans=k-1;
                }else if(xx!=yy){
                    unit(x,y);
                    unit(x+2*N,y+2*N);
                }
            }else{
                int xx=find(x),yy=find(y);
                if(find(x)==find(y)||find(x+2*N)==find(y+2*N)){
                    ans=k-1;
                }else if(xx!=yy){
                    unit(x,y+2*N);
                    unit(x+2*N,y);

                }
            }
        }
        printf("%d
",ans);
    }
    return 0;
}
ural1003
原文地址:https://www.cnblogs.com/sun-yinkai/p/8093273.html