带权并查集(含种类并查集)【经典模板】 例题:①POJ 1182 食物链(经典)②HDU

带权并查集:

增加一个 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 

原文地址:https://www.cnblogs.com/-Ackerman/p/11780355.html