并查集专辑 (poj1182食物链,hdu3038, poj1733, poj1984, zoj3261)

并查集专题训练地址,注册登录了才能看到题目

并查集是一个树形的数据结构,  可以用来处理集合的问题, 也可以用来维护动态连通性,或者元素之间关系的传递(关系必须具有传递性才能有并查集来维护,因为并查集有压缩路径)。

用并查集来维护元素之间关系的传递, 那么元素与元素之间就有一个权值(带权并查集),那么当路径压缩时,我们需要根据关系的传递性来维护

当父元素变动时,关系的变动。

 

poj1182食物链

给定一些元素之间的关系, 问我们有多少是错误的。 当碰到一个错误时,并不会用它来更新并查集(错误的怎么用来更新?),只会把ans++

我们用0表示与父亲是同类,  用1表示吃父亲 , 用2表示被父亲吃。(这表示的是我与父亲的关系,即箭头是由我指向父亲), 那怎么得到父亲与我的关系呢?

只要将箭头的方向改变, 就得到了父亲与我的关系,  (3-rank[x]) % 3  就是了

 只要明白了箭头方向的改变,关系也可以用公式推导出来

那么下边就好办了, 如果判断是否发生错误呢?

如图,红色的线表示题目给我们的关系, 现在他们在一个集合里面,可以通过运算获得蓝线1,然后再通过计算获得蓝线2, 只要将蓝线2和红线对比,就知道

题目所给的关系是否正确。

 

同理,集合的合并也是一样的

通过红线1,依次算出蓝线1,2,3.    那么就可以进行集合的合并了。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <algorithm>
  5 #include <iostream>
  6 #include <queue>
  7 #include <stack>
  8 #include <vector>
  9 #include <map>
 10 #include <set>
 11 #include <string>
 12 #include <math.h>
 13 using namespace std;
 14 #pragma warning(disable:4996)
 15 #pragma comment(linker, "/STACK:1024000000,1024000000")
 16 typedef long long LL;                   
 17 const int INF = 1<<30;
 18 /*
 19 路径压缩的时候要保留结点与结点之间的关系, 所以要维护的一个信息是,结点之间的关系
 20 0同类 ,  1被父亲吃,  2吃父亲
 21 */
 22 const int N = 50000 + 10;
 23 int fa[N];
 24 int id[N];//id[u] 表示与父亲之间的边的权值,, 权值为0,1,2三种
 25 int find(int x)
 26 {
 27     if (x == fa[x])
 28         return x;
 29     //路径压缩的时候,由于父亲的改变, 需要改变权值
 30     int tmp = fa[x];
 31     fa[x] = find(fa[x]);
 32     id[x] = (id[x] + id[tmp]) % 3;
 33     return fa[x];
 34 }
 35 int main()
 36 {
 37     int n, m, ans;
 38     int d, a, b;
 39     int fx, fy;
 40     //while (scanf("%d%d", &n, &m) != EOF)
 41     {
 42         scanf("%d%d", &n, &m);
 43         ans = 0;
 44         for (int i = 1; i <= n; ++i)
 45         {
 46             fa[i] = i;
 47             id[i] = 0;
 48         }
 49         while (m--)
 50         {
 51             scanf("%d%d%d", &d, &a, &b);
 52             if (d == 1)
 53             {
 54                 if (a > n || b > n)
 55                     ans++;
 56                 else
 57                 {
 58                     fx = find(a);
 59                     fy = find(b);
 60                     if (fx != fy)
 61                     {
 62                         fa[fy] = fx;
 63                         id[fy] = ((3 - id[b])%3 + (d - 1) + id[a]) % 3;
 64                     }
 65                     else
 66                     {
 67                         if ((3-id[a]+id[b])%3 != 0)
 68                             ans++;
 69 
 70                     }
 71                 }
 72             }
 73             else
 74             {
 75                 if (a > n || b > n)
 76                     ans++;
 77                 else
 78                 if (a == b)
 79                     ans++;
 80                 else
 81                 {
 82                     fx = find(a);
 83                     fy = find(b);
 84                     if (fx != fy)
 85                     {
 86                         fa[fy] = fx;
 87                         id[fy] = ((3 - id[b])%3  + (d - 1) + id[a]) % 3;
 88                     }
 89                     else
 90                     {
 91                         if ((3 - id[a] + id[b])%3 != 1)
 92                             ans++;
 93                     }
 94                 }
 95             }
 96         }
 97         printf("%d\n", ans);
 98     }
 99     return 0;
100 }
View Code

 

 

hdu3038

有连续的一段数,数字是什么谁都不知道,  但是呢有限制条件。

ai bi c 表示  下标ai到bi的这一段数的和为c

现在给我们m个限制条件,问我们有多少个限制条件与前面的条件矛盾

分析:依旧考察关系的传递, 只不过关系需要转换。  即 sum[bi] - sum[ai-1] = c

然后ai-1作为父亲,  bi作为儿子, 权值为两者的差值。

其余的都和食物链那题差不多了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 #pragma comment(linker, "/STACK:1024000000,1024000000")
16 typedef long long LL;                   
17 const int INF = 1<<30;
18 /*
19 给定一些约束条件, 问有多少个错误的
20 */
21 const int N = 200000 + 10;
22 int fa[N], r[N];
23 int find(int x)
24 {
25     if (x == fa[x])return x;
26     int f = fa[x];
27     fa[x] = find(fa[x]);
28     r[x] = r[x] + r[f];
29     return fa[x];
30 }
31 int main()
32 {
33     int n, m;
34     int a, b, fx, fy,w;
35     while (scanf("%d%d", &n, &m) != EOF)
36     {
37         for (int i = 0; i <= n; ++i)
38         {
39             fa[i] = i;
40             r[i] = 0;
41         }
42         int ans = 0;
43         while (m--)
44         {
45             scanf("%d%d%d", &a, &b,&w);
46             a--;
47             fx = find(a);
48             fy = find(b);
49             if (fx != fy)
50             {
51                 fa[fy] = fx;
52                 r[fy] = w + r[a] - r[b];
53             }
54             else if (r[b] - r[a] != w)
55                 ans++;
56         }
57         printf("%d\n", ans);
58     }
59     return 0;
60 }
View Code

 

poj1733

有长度为n的01序列,  有m个限制条件

ai bi  even/odd   表示从ai到bi, 1的个数为偶数/奇数

限制条件有可能相互矛盾,  题目要我们求出从哪里开始出现矛盾

和上面那题一条的,  cnt[bi] - cnt[ai-1]  表示从ai到bi的1的个数

然后ai-1作为父亲, bi作为儿子, 如果两者相减是偶数, 那么权值为0,如果是奇数, 权值为1.

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 #pragma warning(disable:4996)
14 #pragma comment(linker, "/STACK:1024000000,1024000000")
15 typedef long long LL;
16 const int INF = 1 << 30;
17 /*
18 
19 */
20 std::map<int, int> ma;
21 const int N = 20000 + 10;
22 int fa[N], rank[N];
23 int find(int x)
24 {
25     if (x == fa[x])
26         return x;
27     int f = fa[x];
28     fa[x] = find(fa[x]);
29     rank[x] = (rank[x] + rank[f]) % 2;
30     return fa[x];
31 }
32 int main()
33 {
34     int n, m;
35     int a, b, fx, fy, d;
36     char str[11];
37     scanf("%d%d", &n, &m);
38     for (int i = 0; i < N; ++i)
39     {
40         fa[i] = i;
41         rank[i] = 0;
42     }
43     int cnt = 0;
44     int i;
45     for (i = 0; i < m; ++i)
46     {
47         scanf("%d%d%s", &a, &b, str);
48         a--;
49         if (ma[a] == 0)
50             ma[a] = ++cnt;
51         a = ma[a];
52         if (ma[b] == 0)
53             ma[b] = ++cnt;
54         b = ma[b];
55         if (str[0] == 'o')
56             d = 1;
57         else
58             d = 0;
59         fx = find(a);
60         fy = find(b);
61         if (fx != fy)
62         {
63             fa[fx] = fy;
64             rank[fx] = (rank[a] + rank[b] + d) % 2;
65         }
66         else if ((rank[a] + rank[b]) % 2 != d)
67             break;
68     }
69     printf("%d\n", i);
70     return 0;
71 }
View Code

 

 

poj1984

n个点, m行条件,  每行 F1 F2 L E/W/N/S 表示F2在F1的E/W/N/S方向, 且距离为L

然后有q个询问,每个询问F1 F2 i  表示在已知前i个条件的情况下,求F1和F2的笛卡尔距离。如果无法得到输出-1

分析:其实也是维护元素与元素之间的关系, 只不过关系是他们的笛卡尔距离, 同样的, 这种关系同样具有传递性

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <algorithm>
  5 #include <iostream>
  6 #include <queue>
  7 #include <stack>
  8 #include <vector>
  9 #include <map>
 10 #include <set>
 11 #include <string>
 12 #include <math.h>
 13 using namespace std;
 14 #pragma warning(disable:4996)
 15 #pragma comment(linker, "/STACK:1024000000,1024000000")
 16 typedef long long LL;                   
 17 const int INF = 1<<30;
 18 /*
 19 这次用并查集来维护的依旧是元素与元素之间的关系, 只不过关系是笛卡尔距离
 20 */
 21 const int N = 40000 + 10;
 22 int fa[N], rx[N], ry[N];
 23 int F1[N], F2[N], L[N], D[N];
 24 int find(int x)
 25 {
 26     if (x == fa[x]) return x;
 27     int f = fa[x];
 28     fa[x] = find(fa[x]);
 29     rx[x] = (rx[x] + rx[f]);
 30     ry[x] = (ry[x] + ry[f]);
 31     return fa[x];
 32 }
 33 
 34 int main()
 35 {
 36     int n, m, q;
 37     char str[3];
 38     int x, y, k, fx, fy;
 39     int i;
 40     while (scanf("%d%d", &n, &m) != EOF)
 41     {
 42         for ( i = 1; i <= n; ++i)
 43         {
 44             fa[i] = i;
 45             rx[i] = ry[i] = 0;
 46         }
 47         for ( i = 1; i <= m; ++i)
 48         {
 49             //E 用0表示
 50             scanf("%d%d%d%s", &F1[i], &F2[i], &L[i], str);
 51             if (str[0] == 'W')
 52             {
 53                 swap(F1[i], F2[i]);
 54                 D[i] = 0;
 55             }
 56             else if (str[0] == 'E')
 57                 D[i] = 0;
 58             else if (str[0] == 'N')
 59             {
 60                 swap(F1[i], F2[i]);
 61                 D[i] = 1;
 62             }
 63             else
 64                 D[i] = 1;
 65         }
 66         scanf("%d", &q);
 67         i = 1;
 68         while (q--)
 69         {
 70             scanf("%d%d%d", &x, &y, &k);
 71             for (; i <= k; ++i)
 72             {
 73                 fx = find(F1[i]);
 74                 fy = find(F2[i]);
 75                 if (fx != fy)
 76                 {
 77                     fa[fy] = fx;
 78                     if (D[i] == 0)
 79                     {
 80                         //L[i] - rx[F2[i]] 得到fy与F1[i]的相对距离
 81                         //再加上F1[i]与fx的相对距离,得到了 fy与fx的相对距离
 82                         rx[fy] = rx[F1[i]] + L[i] - rx[F2[i]];
 83                         ry[fy] = ry[F1[i]] + 0 - ry[F2[i]];
 84                     }
 85                     else
 86                     {
 87                         rx[fy] = rx[F1[i]] + 0 - rx[F2[i]];
 88                         ry[fy] = ry[F1[i]] + L[i] - ry[F2[i]];
 89                     }
 90                 }
 91             }
 92             fx = find(x);
 93             fy = find(y);
 94             if (fx != fy)
 95                 puts("-1");
 96             else
 97             {
 98                 int ans = abs((rx[y] - rx[fy]) - (rx[x] - rx[fx])) + abs((ry[y] - ry[fy]) - (ry[x] - ry[fx]));
 99                 printf("%d\n", ans);
100             }
101         }
102 
103     }
104     return 0;
105 }
View Code

 

 

逆向并查集,离线存储后,逆向过来建并查集

zoj3261

给定n个星球, 每个星球有能量值pi,
给定m条边, 表示
然后有两种操作  query a     找到与a连通的能力最大的星球(这个星球的能力要比a大),如果有多个最大的,那么输出编号最小的

destory a b 摧毁a和b之间的道路

并查集要删边很困难,但是我们可以逆向来建并查集。且维护一个集合的父亲为这个集合中权值最大的元素,如果权值相同则设编号小的为父亲

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <algorithm>
  5 #include <iostream>
  6 #include <queue>
  7 #include <stack>
  8 #include <vector>
  9 #include <map>
 10 #include <set>
 11 #include <string>
 12 #include <math.h>
 13 #include <unordered_map>
 14 #include <unordered_map>
 15 using namespace std;
 16 #pragma warning(disable:4996)
 17 #pragma comment(linker, "/STACK:1024000000,1024000000")
 18 typedef long long LL;                   
 19 const int INF = 1<<30;
 20 /*
 21 给定n个星球, 每个星球有能量值pi,
 22 给定m条边, 表示
 23 然后有两种操作  query a     找到与a连通的能力最大的星球(这个星球的能力要比a大),如果有多个最大的,那么输出编号最小的
 24 
 25 
 26 
 27 找到连通的??? 
 28 set<pair<int,int> >;
 29 
 30 lower_bound(
 31 */
 32 const int N = 10000 + 1;
 33 int val[N];
 34 int fa[N];
 35 int u[N * 2], v[N * 2];
 36 int a[N * 5], b[N * 5], ans[N * 5];
 37 unordered_map<int, int> ma[N];//要开一个数组,
 38 int find(int x)
 39 {
 40     if (fa[x] == x)
 41         return x;
 42     return fa[x] = find(fa[x]);
 43 }
 44 void unionSet(int x, int y)
 45 {
 46     int fx = find(x);
 47     int fy = find(y);
 48     if (fx != fy)
 49     {
 50         if (val[fx] > val[fy])
 51             fa[fy] = fx;
 52         else if (val[fx] < val[fy])
 53             fa[fx] = fy;
 54         else
 55         {
 56             if (fx < fy)
 57                 fa[fy] = fx;
 58             else
 59                 fa[fx] = fy;
 60         }
 61     }
 62 }
 63 void input(int &x)
 64 {
 65     char ch = getchar();
 66     while (ch<'0' || ch>'9')
 67         ch = getchar();
 68     x = 0;
 69     while (ch >= '0' &&ch <= '9')
 70     {
 71         x = x * 10 + ch - '0';
 72         ch = getchar();
 73     }
 74 }
 75 int main()
 76 {
 77     int n, q, m;
 78     char query[10];
 79     int t = 1;
 80     while (scanf("%d", &n) != EOF)
 81     {
 82         if (t != 1)
 83             puts("");
 84         t++;
 85         
 86         for (int i = 0; i < n; ++i)
 87         {
 88             ma[i].clear();
 89             input(val[i]);
 90             fa[i] = i;
 91         }
 92         input(m);
 93         for (int i = 0; i < m; ++i)
 94         {
 95             //scanf("%d%d", &u[i], &v[i]);
 96             input(u[i]);
 97             input(v[i]);
 98             if (u[i] > v[i])
 99                 swap(u[i], v[i]);
100         }
101         input(q);
102         for (int i = 0; i < q; ++i)
103         {
104             scanf("%s", query);
105             if (query[0] == 'q')
106             {
107                 a[i] = -1;
108                 input(b[i]);
109             }
110             else
111             {
112                 input(a[i]);
113                 input(b[i]);
114                 if (a[i] > b[i])
115                     swap(a[i], b[i]);
116                 ma[a[i]][b[i]] = 1;
117             }
118         }
119         for (int i = 0; i < m; ++i)
120         {
121             if (ma[u[i]][v[i]]!=1)
122                 unionSet(u[i], v[i]);
123         }
124         for (int i = q - 1; i >= 0; --i)
125         {
126             if (a[i] == -1)
127             {
128                 int fx = find(b[i]);
129                 if (val[fx] > val[b[i]])
130                     ans[i] = fx;
131                 else
132                     ans[i] = -1;
133             }
134             else
135             {
136                 ans[i] = -10;
137                 unionSet(a[i], b[i]);
138             }
139         }
140         for (int i = 0; i < q; ++i)
141         if (ans[i] != -10)
142             printf("%d\n", ans[i]);
143     }
144     return 0;
145 }
View Code

 

转载于:https://www.cnblogs.com/justPassBy/p/4693071.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值