带权并查集:
增加一个 value 值,并且每次合并和查找的时候需要去维护这个 value
例题一 :POJ 1182 食物链(经典)
题目链接:https://vjudge.net/contest/339425#problem/E
带权并查集的解法
定义两个数组fa[ ]和rela[ ],fa用来判断集合关系,rela用来描述其与根节点的关系。因为关系满足传递性,所以可以推导出给出条件下的当前关系,在判断与之前已有关系是否矛盾。
本题的解法巧妙地利用了模运算,rela数组用0表示同类,1表示当前点能吃别人,2表示当前点被别人吃。
再注意一点:在本题中需要注意的是传入的relation恰为描述的种类号减一。也就是说要提前将输入的描述号做-1处理后传入函数中
1 #include <iostream> 2 #include <stdio.h> 3 using namespace std; 4 5 const int MAXN = 50005; 6 7 int fa[MAXN], rela[MAXN]; 8 int n, k, ans; 9 10 void init() 11 { 12 for(int i = 1; i <= n; i++) 13 { 14 fa[i] = i; 15 rela[i] = 0; 16 } 17 ans = 0; 18 } 19 20 int find(int x) 21 { 22 if(x == fa[x]) 23 return x; 24 else 25 { 26 int temp = fa[x]; 27 fa[x] = find(fa[x]); 28 rela[x] = (rela[x] + rela[temp]) % 3; 29 return fa[x]; 30 } 31 } 32 33 //以下的r均为处理过的relation,0表示同类,1表示x吃y,2表示x被y吃 34 //也就是说这里的r为题目所给编号-1 35 void merge(int r, int x, int y) 36 { 37 int fx = find(x), fy = find(y); 38 if(fx != fy) 39 { 40 fa[fx] = fy; 41 rela[fx] = (rela[y] - rela[x] + r + 3) % 3;//+3是为了防止这里相减产生负数 42 } 43 } 44 45 bool check(int r, int x, int y) 46 { 47 if(x > n || y > n) 48 return false; 49 if(r == 1 && x == y) 50 return false; 51 if(find(x) == find(y)) 52 { 53 int relation = ((rela[x] - rela[y]) % 3 + 3) % 3;//根据前面正确描述得出的正确的关系 54 return relation == r; 55 } 56 else 57 return true; 58 } 59 60 int main() 61 { 62 scanf("%d%d", &n, &k); 63 init(); 64 while(k--) 65 { 66 int a, b, c; 67 scanf("%d%d%d", &a, &b, &c); 68 a--; 69 if(check(a, b, c)) 70 merge(a, b, c); 71 else 72 ans++; 73 } 74 printf("%d ", ans); 75 return 0; 76 }
拆点并查集的解法可以看:https://www.cnblogs.com/-Ackerman/p/11779482.html
例题二 HDU - 1829 A bug’s life(简单)
题目链接:https://vjudge.net/contest/339425#problem/J
带权并查集解法
很明显和上面的思路相同,只不过由3种关系变成了2种关系
那么只需要将mod改为2 即可。
1 #include <math.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <iostream> 5 #include <algorithm> 6 #include <string> 7 #include <string.h> 8 #include <vector> 9 #include <map> 10 #include <stack> 11 #include <set> 12 //#include <random> 13 14 #define LL long long 15 #define INF 0x3f3f3f3f 16 #define ls nod<<1 17 #define rs (nod<<1)+1 18 const int maxn = 2e5 + 10; 19 const double eps = 1e-9; 20 21 int pre[maxn],rel[maxn]; 22 bool flag; 23 24 void init(int n) { 25 for (int i=0;i<=n;i++) { 26 pre[i] = i; 27 rel[i] = 0; 28 } 29 flag = false; 30 } 31 32 int find(int x) { 33 if (pre[x] != x) { 34 int t = pre[x]; 35 pre[x] = find(pre[x]); 36 rel[x] = (rel[x] + rel[t]) % 2; 37 } 38 return pre[x]; 39 } 40 41 void merge(int r,int x,int y) { 42 int rootx = find(x),rooty = find(y); 43 if (rootx != rooty) { 44 pre[rootx] = rooty; 45 rel[rootx] = (rel[y] - rel[x] + r + 2) % 2; 46 } 47 } 48 49 bool check(int r,int x,int y) { 50 int rootx = find(x),rooty = find(y); 51 if (rootx == rooty) { 52 int relation = (rel[x] - rel[y] + 2) % 2; 53 return relation == r; 54 } 55 else 56 return true; 57 } 58 59 int main() { 60 //freopen("../in.txt","r",stdin); 61 int T; 62 scanf("%d",&T); 63 int t = 1; 64 while (T--) { 65 int n,m; 66 scanf("%d%d",&n,&m); 67 init(n); 68 while (m--) { 69 int x,y; 70 scanf("%d%d",&x,&y); 71 if (check(1,x,y)) { 72 merge(1,x,y); 73 } 74 else 75 flag = true; 76 } 77 printf("Scenario #%d: ",t++); 78 if (flag) { 79 printf("Suspicious bugs found! "); 80 } 81 else 82 printf("No suspicious bugs found! "); 83 } 84 }
例题三:hihoCoder 1515 : 分数调查
题目链接:https://vjudge.net/problem/HihoCoder-1515
思路:
同样使用带权并查集,用数组val记录同学们的权值(与根的分数差,比根高出多少分),那么
路径压缩时(find):val[x] = val[x] + val[temp];(temp为x的父节点)
合并时(merge): val[fx] = val[y] - val[x] + s (这里S是x->y的关系,符合公式)
那么要求x比y高多少分,直接输出 val[x] - val[y]即可。
1 #include <math.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <iostream> 5 #include <algorithm> 6 #include <string> 7 #include <string.h> 8 #include <vector> 9 #include <map> 10 #include <stack> 11 #include <set> 12 //#include <random> 13 14 #define LL long long 15 #define INF 0x3f3f3f3f 16 #define ls nod<<1 17 #define rs (nod<<1)+1 18 const int maxn = 2e5 + 10; 19 const double eps = 1e-9; 20 21 int pre[maxn],val[maxn]; 22 23 void init(int n) { 24 for (int i=0;i<=n;i++) { 25 pre[i] = i; 26 val[i] = 0; 27 } 28 } 29 30 int find(int x) { 31 if (pre[x] != x) { 32 int t = pre[x]; 33 pre[x] = find(pre[x]); 34 val[x] += val[t]; 35 } 36 return pre[x]; 37 } 38 39 void merge(int x,int y,int v) { 40 int rootx = find(x),rooty = find(y); 41 if (rootx != rooty) { 42 pre[rootx] = rooty; 43 val[rootx] = val[y] - val[x] + v; 44 } 45 } 46 47 int main() { 48 //freopen("../in.txt","r",stdin); 49 int n,m,q; 50 scanf("%d%d%d",&n,&m,&q); 51 init(n); 52 for (int i=1;i<=m;i++) { 53 int x,y,v; 54 scanf("%d%d%d",&x,&y,&v); 55 merge(x,y,v); 56 } 57 while (q--) { 58 int x,y; 59 scanf("%d%d",&x,&y); 60 int rootx = find(x),rooty = find(y); 61 if (rootx == rooty) { 62 printf("%d ",val[x] - val[y]); 63 } 64 else 65 printf("-1 "); 66 } 67 return 0; 68 }
当然后面两题也可以采用拆点并查集的解法可以参看这个博客:https://blog.csdn.net/floraqiu/article/details/79226320