路径压缩+按秩合并
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int maxn=500010; 6 int a[maxn], n; 7 int fa[maxn]; 8 int rank[maxn]; 9 10 void init() { 11 for(int i=1;i<=n; i++) { 12 fa[i]=i; 13 rank[i]=0; 14 } 15 return ; 16 } 17 18 int find(int z) { 19 if(fa[z]!=z) 20 return fa[z]=find(fa[z]); 21 return fa[z]; 22 } 23 24 void unionn(int x,int y) { 25 x=find(x); 26 y=find(y); 27 if (x==y) return ; 28 if(rank[x]<rank[y]) { 29 fa[x]=y; 30 } 31 else { 32 fa[y]=x; 33 if (rank[x]==rank[y]) 34 rank[x]++; 35 } 36 return; 37 }
例题
洛谷T3766
//find与fa引发的血案
题目描述
有一个 M 行 N 列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。
输入输出格式
输入格式:
第一行输入两个正整数 m 和 n。
以下若干行每行四个正整数 x1,y1,x2,y2, 表示第 x1 行第 y1 列的点和第 x2 行第 y2 列的点已经有连线。输入保证|x1-x2|+|y1-y2|=1。
输出格式:
输出使得连通所有点还需要的最小花费。
输入输出样例
输入样例#1:
2 2 1 1 2 1
输出样例#1:
3
说明
30%数据: n*m<=1000
100%数据: m,n<=1000
标程
//find与fa引发的血案
1 #include <iostream>
2 #include <cstdio>
3 using namespace std;
4 int m, n, tot, ans;
5 int fa[1000010]; //father x,y=fa[x*1000+y]
6
7 int find(int z) {
8 if (fa[z]!=z) fa[z]=find(fa[z]);
9 return fa[z];
10 }
11
12 int unionn(int x, int y) {
13 x=find(x);
14 y=find(y);
15 if (x!=y) fa[x]=y;
16 }
17
18 int main() {
19 cin >> m >> n;
20 int sum = m*n;
21 for (int i=1; i<=sum; i++) fa[i]=i;
22 int x1,y1,x2,y2;
23 while (cin>>x1>>y1>>x2>>y2) {
24 unionn((y1-1)*m+x1,(y2-1)*m+x2);
25 }
26 for (int i=1; i<=sum; i++)
27 if (fa[i]==i) tot++;
28 if(tot==1) { cout<<ans; return 0; }
29 for (int i=1; i<=n; i++) { //对于每一列A[i]
30 for (int j=1; j<m; j++) { //枚举a[i][j]&a[i][j+1]是否联通
31 int k=(i-1)*m+j;
32 if (find(k)!=find(k+1)) {
33 tot--;
34 ans++;
35 unionn(k,k+1);
36 }
37 if (tot==1) { cout<<ans; return 0; }
38 }
39 }
40 for (int i=1; i<n; i++) {
41 if (find((i-1)*m+1)!=find(i*m+1)) {
42 tot--;
43 ans+=2;
44 unionn((i-1)*m+1,i*m+1);
45 }
46 if (tot==1) { cout<<ans; return 0; }
47 }
48 cout<<"0";
49 return 0;
50 }