并查集专辑 (poj1182食物链,hdu3038, poj1733, poj1984, zoj3261)
并查集是一个树形的数据结构, 可以用来处理集合的问题, 也可以用来维护动态连通性,或者元素之间关系的传递(关系必须具有传递性才能有并查集来维护,因为并查集有压缩路径)。
用并查集来维护元素之间关系的传递, 那么元素与元素之间就有一个权值(带权并查集),那么当路径压缩时,我们需要根据关系的传递性来维护
当父元素变动时,关系的变动。
给定一些元素之间的关系, 问我们有多少是错误的。 当碰到一个错误时,并不会用它来更新并查集(错误的怎么用来更新?),只会把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 }
有连续的一段数,数字是什么谁都不知道, 但是呢有限制条件。
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 }
有长度为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 }
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 }
逆向并查集,离线存储后,逆向过来建并查集
给定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 }