并查集,以及可拆分并查集

普通并查集. 合并 均摊O(log(n)) 查询 均摊O(log(n))

 1 //常用版本
 2 
 3 //Union Find
 4 int f[1005000];
 5 
 6 void INIT(int size)
 7 { for(int i=0;i<=size;i++) f[i]=i; }
 8 
 9 int findf(int x) 
10 { return f[x]==x ? x : f[x]=findf(f[x]); };
11 
12 int connect(int x)
13 { f[findf(a)]=findf(b); }

 VIJOS 1443 会卡掉不加启发式的并查集.

 

优化并查集. 加上了启发式合并. 复杂度: 合并 均摊O(α(n)); 查询 均摊O(α(n)). α函数是Ackerman函数的反函数.一般认为其小于等于4.

 1 //union find
 2 int f[5000],r[5000];
 3 inline void INIT(int size)
 4 { for(int i=0;i<size;i++) f[i]=i,r[i]=1; }
 5 
 6 int findf(int x)
 7 { return f[x]==x ? x : f[x]=findf(f[x]); }
 8 
 9 inline void connect(int a,int b)
10 {
11     if(r[a]>r[b]) swap(a,b);
12     int fa=findf(a);
13     int fb=findf(b);
14     r[fb]+=r[fa];
15     f[fa]=fb;
16 }

 

使用r数组表示"秩",即集合大小,存储在每个集合的根节点.

合并的时候将秩小的集合的根节点的父亲设到秩大的集合的根.

常数较上者而言更加大.

 

另外,如果离线处理询问,那么可以通过建图然后FloodFill的方式达到严格O(n)预处理+O(1)询问,常数较大.

 

可拆分并查集.

最坏情况: 合并 O(n) 查询 O(n) 拆分 O(n)

平均情况(对于随机生成的数据): 合并 O(logn) 查询 O(logn) 拆分 O(logn)

对于某种方式生成的随机数据跑得很快的样子?

反正随便卡.....

 

 1 //union find
 2 int f[1050];
 3 void INIT(int size)
 4 { for(int i=0;i<=size;i++) f[i]=i; }
 5 
 6 int findf(int x)
 7 { while(f[x]!=x) x=f[x]; return x; }
 8 
 9 void setroot(int x)
10 { if(f[x]!=x) setroot(f[x]); f[x]=f[f[x]]=x; }
11 
12 void link(int a,int b)
13 { setroot(a); f[a]=b; }
14 
15 void cut(int a,int b)
16 { setroot(a); f[b]=b; }

 

 

 

可以看到后边这种比较像LCT...

 

链式并查集.

复杂度 均摊 O(nlogn)

AC VIJOS 1443 银河英雄传说

常数好大....复杂度也好大.......这道题用了1653ms.....

这个并查可以用于维护一些奇怪的东西..... (做这题的时候脑子里全是LCT怎么办)

 1 //Union Find
 2 int L[30050]; //left node pointer.
 3 int R[30050]; //right node pointer.
 4 int C[30050]; //center node.
 5 int tot[30050]; //chain nodes amount.
 6 int nxt[30050]; //next node.
 7 int v[30050]; //value of all nodes.
 8 
 9 void INIT(int size)
10 {
11     for(int i=0;i<=size;i++)
12     L[i]=R[i]=C[i]=i,tot[i]=1,nxt[i]=-1;
13 }
14 
15 void Merge(int x,int y)
16 {
17     //link x to y's tail.
18     int cx=C[x];
19     int cy=C[y];
20     if(tot[cx]>tot[cy]) //change chain y's info
21     {
22         for(int i=L[cy];i!=-1;i=nxt[i])
23             C[i]=cx; //chagne center node.
24         nxt[R[cy]]=L[cx];
25         L[cx]=L[cy];
26         tot[cx]+=tot[cy];
27     }
28     else //change chain x's info.
29     {
30         for(int i=L[cx];i!=-1;i=nxt[i])
31             C[i]=cy; //change center node.
32         nxt[R[cy]]=L[cx];
33         R[cy]=R[cx];
34         tot[cy]+=tot[cx];
35     }
36 }
37 
38 
39 char op[505000];
40 int a[505000];
41 int b[505000];
42 
43 int n=30000,m;
44 
45 int main()
46 {
47     m=getint();
48     
49     INIT(n);
50     
51     for(int i=0;i<m;i++)
52     {
53         char inp[20];
54         scanf("%s",inp);
55         op[i]=inp[0];
56         a[i]=getint()-1;
57         b[i]=getint()-1;
58     }
59     
60     //first operations dealing.
61     for(int i=0;i<m;i++)
62     if(op[i]=='M')
63         Merge(a[i],b[i]);
64     
65     //tag nodes' value.
66     for(int i=0;i<n;i++)
67     if(v[i]==0)
68     {
69         int cnt=0;
70         for(int j=L[C[i]];j!=-1;j=nxt[j])
71         v[j]=++cnt;
72     }
73     
74     INIT(n);
75     
76     //second operations dealing.
77     for(int i=0;i<m;i++)
78     {
79         if(op[i]=='M')
80             Merge(a[i],b[i]);
81         else
82         {
83             if(C[a[i]]==C[b[i]])
84             printf("%d\n",abs(v[a[i]]-v[b[i]])-1);
85             else
86             printf("-1\n");
87         }
88     }
89     
90     return 0;
91 }
View Code

做法就是对每一条链维护一个"中心节点",这个节点存储了链的总结点数,链的左端点和链的右端点.

合并的时候把这些存储的值的转移安排好就行了.......

实在不懂就看代码......

千万注意Merge函数中nxt数组赋值的顺序......

next数组的值一定要在cx赋值完毕以后修改,不然赋值操作会把一整个集合扫一遍!....

这样总时间复杂度就是O(n^2)的了....害我T了一发 TAT

AC BZOJ 1483 又是按秩合并.....

全都是调试用的注释的版本

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21 typedef long double ldb;
 22  
 23 using namespace std;
 24  
 25 inline int getint()
 26 {
 27     int res=0;
 28     char c=getchar();
 29     bool mi=false;
 30     while((c<'0' || c>'9')/* && !feof(stdin)*/) mi=(c=='-'),c=getchar();
 31     while('0'<=c && c<='9'/* && !feof(stdin)*/) res=res*10+c-'0',c=getchar();
 32     return mi ? -res : res;
 33 }
 34 inline ll getll()
 35 {
 36     ll res=0;
 37     char c=getchar();
 38     bool mi=false;
 39     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 40     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 41     return mi ? -res : res;
 42 }
 43 
 44 //==============================================================================
 45 //==============================================================================
 46 //==============================================================================
 47 //==============================================================================
 48 
 49 
 50 int C[105000];    //color array.
 51 int N[1005000]; //store the amount of same-color node.
 52 int F[105000]; //father(center) node of this color
 53 int L[105000]; //what color do this node actually have.
 54 int R[105000]; //root of a chain. 
 55 
 56 int p[1005000]; //pointer to the previous same-color node.
 57 int t[1005000]; //true color of "color code".
 58 
 59 int n,m;
 60 int res;
 61 
 62 void ChangeColor(int i,int color)
 63 {
 64     if(C[i-1]!=C[i] && C[i-1]==color) res--;
 65     if(C[i+1]!=C[i] && C[i+1]==color) res--;
 66     if(C[i-1]==C[i] && C[i-1]!=color) res++;
 67     if(C[i+1]==C[i] && C[i+1]!=color) res++;
 68     C[i]=color;
 69 }
 70 
 71 
 72 int main()
 73 {
 74     freopen("in.txt","r",stdin);
 75     freopen("out.txt","w",stdout);
 76     
 77     n=getint();
 78     m=getint();
 79     C[0]=C[n+1]=-1;
 80     for(int i=1;i<=n;i++) N[C[i]=getint()]++;
 81     for(int i=1;i<=n;i++) res+=( C[i]!=C[i-1] );
 82     for(int i=1;i<=n;i++) { F[i]=p[C[i]]; p[C[i]]=i; }
 83     for(int i=1;i<=n;i++) R[i]=( F[i] ? R[F[i]]:i );
 84     
 85     for(int i=0;i<=1000000;i++) t[i]=i;
 86     
 87     //for(int i=1;i<=n;i++) 
 88     //printf("i:%d color:%d f:%d cnt:%d root:%d l:%d\n",i,C[i],F[i],N[i],R[i],L[i]);
 89     
 90     
 91     //printf("p:");for(int i=1;i<9;i++) printf("%d ",p[i]); printf("\n");
 92     //printf("t:");for(int i=1;i<9;i++) printf("%d ",t[i]); printf("\n");
 93     //printf("C:");for(int j=1;j<=n;j++) printf("%d ",C[j]); printf("\n");
 94     
 95     for(int i=0;i<m;i++)
 96     {
 97         int c=getint();
 98         if(c==1)
 99         {
100             int a=getint();
101             int b=getint(); //change all color a into b.
102             if(a==b) continue;
103             //printf("true color of %d is %d\ntrue color of %d is %d\n",a,t[a],b,t[b]);
104             if(N[t[a]]>N[t[b]]) swap(t[a],t[b]);
105             //printf("change color %d[%d] to %d[%d]\n",t[a],R[p[t[a]]],t[b],R[p[t[b]]]);
106             a=t[a]; b=t[b];
107             
108             int x=p[a],y=p[b]; if(!p[a]) goto FINAL; //continue;
109             printf("%d %d\n",x,y);
110             N[b]+=N[a]; N[a]=0; p[a]=0; p[b]=x;
111             while(F[x]){ R[x]=R[y]; ChangeColor(x,b); x=F[x]; }
112             R[x]=R[y]; ChangeColor(x,b); F[x]=y;
113         }
114         else if(c==2) printf("%d\n",res);
115         
116         /*
117         FINAL: if(c!=2) {
118         for(int i=1;i<=n;i++) if(p[t[i]]){ printf("color:%d .",t[i]); 
119         for(int j=p[t[i]];j;j=F[j])printf("->%d",j);  printf("\n");}
120         printf("p:");for(int i=1;i<=n;i++) printf("%d ",p[i]); printf("\n");
121         printf("t:");for(int i=1;i<=n;i++) printf("%d ",t[i]); printf("\n");
122         printf("C:");for(int j=1;j<=n;j++) printf("%d ",C[j]); printf("\n"); }
123         */
124     }
125     
126     
127     return 0;
128 }
129 
130 
131 /*
132 10 2
133 1 2 1 3 3 1 1 2 2 1
134 1 2
135 2 3
136 */
View Code

不带注释的版本

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21 typedef long double ldb;
 22  
 23 using namespace std;
 24  
 25 inline int getint()
 26 {
 27     int res=0;
 28     char c=getchar();
 29     bool mi=false;
 30     while((c<'0' || c>'9')/* && !feof(stdin)*/) mi=(c=='-'),c=getchar();
 31     while('0'<=c && c<='9'/* && !feof(stdin)*/) res=res*10+c-'0',c=getchar();
 32     return mi ? -res : res;
 33 }
 34 inline ll getll()
 35 {
 36     ll res=0;
 37     char c=getchar();
 38     bool mi=false;
 39     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 40     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 41     return mi ? -res : res;
 42 }
 43 
 44 //==============================================================================
 45 //==============================================================================
 46 //==============================================================================
 47 //==============================================================================
 48 
 49 
 50 int C[105000];    //color array.
 51 int N[1005000]; //store the amount of same-color node.
 52 int F[105000]; //father(center) node of this color
 53 int L[105000]; //what color do this node actually have.
 54 int R[105000]; //root of a chain. 
 55 
 56 int p[1005000]; //pointer to the previous same-color node.
 57 int t[1005000]; //true color of "color code".
 58 
 59 int n,m;
 60 int res;
 61 
 62 void ChangeColor(int i,int color)
 63 {
 64     if(C[i-1]!=C[i] && C[i-1]==color) res--;
 65     if(C[i+1]!=C[i] && C[i+1]==color) res--;
 66     if(C[i-1]==C[i] && C[i-1]!=color) res++;
 67     if(C[i+1]==C[i] && C[i+1]!=color) res++;
 68     C[i]=color;
 69 }
 70 
 71 
 72 int main()
 73 {
 74     n=getint();
 75     m=getint();
 76     C[0]=C[n+1]=-1;
 77     for(int i=1;i<=n;i++) N[C[i]=getint()]++;
 78     for(int i=1;i<=n;i++) res+=( C[i]!=C[i-1] );
 79     for(int i=1;i<=n;i++) { F[i]=p[C[i]]; p[C[i]]=i; }
 80     for(int i=1;i<=n;i++) R[i]=( F[i] ? R[F[i]]:i );
 81     
 82     for(int i=0;i<=1000000;i++) t[i]=i;
 83     
 84     for(int i=0;i<m;i++)
 85     {
 86         int c=getint();
 87         if(c==1)
 88         {
 89             int a=getint();
 90             int b=getint();
 91             if(a==b) continue;
 92             if(N[t[a]]>N[t[b]]) swap(t[a],t[b]);
 93             a=t[a]; b=t[b];
 94             
 95             int x=p[a],y=p[b]; if(!p[a]) continue;
 96             N[b]+=N[a]; N[a]=0; p[a]=0; p[b]=x;
 97             while(F[x]){ R[x]=R[y]; ChangeColor(x,b); x=F[x]; }
 98             R[x]=R[y]; ChangeColor(x,b); F[x]=y;
 99         }
100         else if(c==2) printf("%d\n",res);
101 
102     }
103     
104     return 0;
105 }
View Code

按秩合并呢,总结起来两点:

1.如何存储集合的大小; 2.如何合并两条链.

存储集合的大小,可以直接对每条链维护一个中央节点来存储,然后合并的时候注意一下把数据合并过去.

也可以用一种集合的对应关系(在本题中是颜色)来快速存储与合并.

合并两条链,先把a(我们把较小的那条链叫做a)的全局数据(比如链长)合并到b(另外一条链).

然后扫描a,每扫描到一个点就把它的数据(比如中央节点指针)改掉.

链a的末尾节点要特别处理,把链a末尾接到链b上(或把链b末尾接到链a上).

然后把b的链头改成a的链头,再把a的链头置空(或把a的链头改成b的链头,两个链头都不置空).

链头是否置空以及是否能够左右两次合并要依照题目来看.

 ....

原文地址:https://www.cnblogs.com/DragoonKiller/p/4306169.html