• 带权并查集(含种类并查集)【经典模板】 例题:①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 

  • 相关阅读:
    BZOJ 3631 链剖+差分
    BZOJ 1103 DFS序+线段树
    BZOJ 3629 约数和定理+搜索
    198. House Robber
    152. Maximum Product Subarray
    139. Word Break
    132. Palindrome Partitioning II
    120. Triangle
    115. Distinct Subsequences
    97. Interleaving String
  • 原文地址:https://www.cnblogs.com/-Ackerman/p/11780355.html
Copyright © 2020-2023  润新知