并查集

并查集(Disjoint-Set)是一种可以动态维护若干不重叠集合,并支持合并与查询的数据结构。

普通并查集

处理普通合并问题

代码实现:

int par[maxn];  //存储
void init()
{
    for(int i=1;i<=n;i++) par[i]=i;  //初始化
}
int get(int x)
{
    if(x==par[x]) return x;
    return par[x]=get(par[x]);  //路径压缩,直连树根
}
void Merge(int x,int y)
{
    pa[get(x)]=get(y);  //x的树根作为y的根
}

逆向并查集

 并查集一般用于构建连接,处理断开时就乏力了,为此我们可以先存下所有操作,建立一个全连接图(依题),然后倒序处理操作。

边带权并查集

当两个点之间不仅需要合并,还需要计算距离时,我们就会用到边带权的并查集。

此外,边权还可以用01表示两个关系的互斥,比如POJ1733

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
map<int,int> mp;
int cnt=1;
int par[10010];
int spe[10010]; //0同性,1异性
int get(int x)
{
    if(x==par[x]) return x;
    int rt=get(par[x]);
    spe[x]^=spe[par[x]];
    return par[x]=rt;
}
int Merge(int x, int y, int d)
{
    int fx=get(x);
    int fy=get(y);
    if(fx==fy){
        if(spe[x]^spe[y]!=d) return 1;
        else return 0;
    }
    else{
        par[fy]=fx;
        spe[fy]=(spe[x]+spe[y]+d)%2;
    }
    return 0;
}

int main()
{
    for(int i=0;i<10010;i++) par[i]=i,spe[i]=0;
    ios::sync_with_stdio(false);
    int n,m,x,y,d,ans=0;
    string o;
    mp.clear();
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>o;
        if(!mp[x-1]) mp[x-1]=cnt++;
        if(!mp[y]) mp[y]=cnt++;
        if(o=="even") d=0;
        else d=1;
        if(!ans&&Merge(mp[x-1],mp[y],d)) ans=i;
    }
    if(ans) cout<<ans-1<<endl;
    else cout<<m<<endl;
    return 0;
}
View Code

拓展域并查集

当问题中关系不止一种时,可以拓展域(即拓展并查集par的大小)。

例如POJ1182,题目中出现了三种关系,那我们不妨再拓展出两个域,分别表示天敌(x),同类(n+x)和猎物(2*n+x)。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define ll long long
 6 #define inf 0x3f3f3f3f
 7 using namespace std;
 8 int par[150010];
 9 int get(int x)
10 {
11     if(x==par[x]) return x;
12     return par[x]=get(par[x]);
13 }
14 void Merge(int x,int y)
15 {
16     par[get(x)]=get(y);
17 }
18 int main()
19 {
20     int n,m,ans=0;
21     int o,x,y;
22     scanf("%d%d",&n,&m);
23     for(int i=0;i<=3*n;i++) par[i]=i;
24     while(m--)
25     {
26         scanf("%d%d%d",&o,&x,&y);
27         if(x>n||y>n)
28         {
29             ans++;
30             continue;
31         }
32         if(o==1)
33         {
34             int X1=get(x),X2=get(x+2*n),Y=get(y+n);
35             if(X1==Y||X2==Y) ans++;
36             else
37             {
38                 Merge(x,y);
39                 Merge(x+n,y+n);
40                 Merge(x+2*n,y+2*n);
41             }
42         }
43         else
44         {
45             if(x==y)
46             {
47                 ans++;
48                 continue;
49             }
50             else
51             {
52                 int X1=get(x+n),X2=get(x),Y=get(y+n);
53                 if(X1==Y||X2==Y) ans++;
54                 else
55                 {
56                     Merge(x+n,y);
57                     Merge(2*n+x,y+n);
58                     Merge(x,2*n+y);
59                 }
60             }
61         }
62     }
63     cout<<ans<<endl;
64     return 0;
65 }
View Code

再比如POJ2492,题目有两种关系——同性和异性,我们同样也可以用两个域表示同性(x)和异性(n+x)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
map<int,int> mp;
int cnt=1;
int par[4010];
int get(int x)
{
    if(x==par[x]) return x;
    return par[x]=get(par[x]);
}
void Merg(int x,int y)
{
    par[x]=y;
}
int main()
{
    ios::sync_with_stdio(false);
    int m,t,n,x,y;
    cin>>t;
   for(int ii=1;ii<=t;ii++)
    {
        if(ii-1) cout<<endl;
        cout<<"Scenario #"<<ii<<':'<<endl;
        for(int i=0;i<4010;i++) par[i]=i;
        cin>>n>>m;
        bool f=1;
        while(m--)
        {
            cin>>x>>y;
            if(!f) continue;
            int x1=get(x),x2=get(x+n),y1=get(y+n),y2=get(y);
            if(x1==y2||y1==x2) f=0;
            Merg(x1,y1);
            Merg(x2,y2);
        }
        if(!f) cout<<"Suspicious bugs found!"<<endl;
        else cout<<"No suspicious bugs found!"<<endl;
    }
    return 0;
}
View Code

 

posted @ 2019-07-27 09:05  然墨  阅读(206)  评论(0编辑  收藏  举报