并查集,以及可拆分并查集
普通并查集. 合并 均摊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 }
做法就是对每一条链维护一个"中心节点",这个节点存储了链的总结点数,链的左端点和链的右端点.
合并的时候把这些存储的值的转移安排好就行了.......
实在不懂就看代码......
千万注意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 */
不带注释的版本
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 }
按秩合并呢,总结起来两点:
1.如何存储集合的大小; 2.如何合并两条链.
存储集合的大小,可以直接对每条链维护一个中央节点来存储,然后合并的时候注意一下把数据合并过去.
也可以用一种集合的对应关系(在本题中是颜色)来快速存储与合并.
合并两条链,先把a(我们把较小的那条链叫做a)的全局数据(比如链长)合并到b(另外一条链).
然后扫描a,每扫描到一个点就把它的数据(比如中央节点指针)改掉.
链a的末尾节点要特别处理,把链a末尾接到链b上(或把链b末尾接到链a上).
然后把b的链头改成a的链头,再把a的链头置空(或把a的链头改成b的链头,两个链头都不置空).
链头是否置空以及是否能够左右两次合并要依照题目来看.
....