并查集 总概
合并两个集合
查询某个节点的祖宗的节点
记录每个集合的大小:(绑定到根节点)
每个节点到根节点的距离:(绑定到每个节点上)
1250. 格子游戏 题目链接: 1250. 格子游戏
题解思路: 根据题目,将各个元素映射到一维数组中,连接起来,检查连接的过程中是否出现环(若出现环状-》出现连接的时候两个节点在同一个连通块中)就说明有圈了
题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <bits/stdc++.h> using namespace std;const int N = 41000 ;int p[N];int n, m;int get (int x, int y) { return x * n + y; } int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } int main () { cin >> n >> m; int res = 0 ; for (int i = 0 ; i < n * n; i++) p[i] = i; for (int i = 1 ; i <= m; i++){ int x, y; char d; cin >> x >> y >> d; int a, b; x--, y--; a = get (x, y); if (d == 'D' ) b = get (x + 1 , y); else b = get (x, y + 1 ); int pa = find (a), pb = find (b); if (pa == pb){ res = i; break ; } p[pa] = pb; } if (!res)cout << "draw" << endl; else cout << res << endl; return 0 ; }
1252. 搭配购买 题目链接: 1252. 搭配购买 - AcWing题库
题解思路: 思路:根据题目,将各个搭配连接起来,就相当于各个连通块选择,使得总价为vol,价值最大的01背包问题
题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> using namespace std;const int N = 10010 ;int v[N], w[N], p[N], f[N]; int n, m, vol;int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; } int main () { cin >> n >> m >> vol; for (int i = 1 ; i <= n; i++) cin >> v[i] >> w[i]; for (int i = 1 ; i <= n; i++) p[i] = i; while (m --){ int a, b; cin >> a >> b; int pa = find (a), pb = find (b); if (pa != pb){ v[pb] += v[pa]; w[pb] += w[pa]; p[pa] = p[pb]; } } for (int i = 1 ; i <= n; i++){ if (p[i] == i) for (int j = vol; j >= v[i]; j--){ f[j] = max (f[j], f[j - v[i]] + w[i]); } } cout << f[vol] << endl; return 0 ; }
237. 程序自动分析 题目链接: 237. 程序自动分析
题解思路: 离散化+并查集
因为i,j的范围是1~1e9,所以需要离散化,然后就是基本的,相等的点连接到一个连通图里面,不相等的点进行判断。
离散化:
保序:排序+判重+二分
不保序: map、hash表
此题不保序
题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <bits/stdc++.h> using namespace std;const int N = 200010 ;int n, m, p[N];unordered_map<int , int > mp; struct Q { int x, y, e; }q[N]; int find (int x) { if (p[x] != x)p[x] = find (p[x]); return p[x]; } int get (int x) { if (mp.count (x) == 0 ) mp[x] = ++n; return mp[x]; } int main () { int T; cin >> T; while (T--){ cin >> m; n = 0 ; mp.clear (); for (int i = 1 ; i <= m; i++ ){ int x, y, e; cin >> x >> y >> e; q[i] = {get (x), get (y), e}; } for (int i = 1 ; i <= n; i++) p[i] = i; for (int i = 1 ; i <= m; i++){ if (q[i].e == 1 ){ int pa = find (q[i].x), pb = find (q[i].y); p[pa] = pb; } } int flag = 0 ; for (int i = 1 ; i <= m; i++){ if (q[i].e == 0 ){ int pa = find (q[i].x), pb = find (q[i].y); if (pa == pb) { flag = 1 ; break ; } } } if (flag)cout << "NO" << endl; else cout << "YES" << endl; } return 0 ; }
238. 银河英雄传说 题目链接: 238. 银河英雄传说
题解思路: 并查集:统一维护每个战舰到排头战舰的距离
当我们询问两个战舰间隔的时候是,|dx - dy| - 1 or 0(特判)
让排头当根节点->同时维护一个size表示当前队列的长度
假设有一个初始的队列a,新加一个队列b,就把队列b的排头d[b]+size[a],并且在find函数里面刷新后面所有d[x];
d[x]表示的是x到p[x]的距离,这样就可以很方便的算出每个节点到最排头的距离
题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> using namespace std;const int N = 30010 ;int p[N], d[N], Size[N], T;int find (int x) { if (p[x] != x){ int root = find (p[x]); d[x] += d[p[x]]; p[x] = root; } return p[x]; } int main () { cin >> T; for (int i = 1 ; i < N; i++) p[i] = i, Size[i] = 1 ; while (T--){ char op; int a, b; cin >> op >> a >> b; if (op == 'M' ){ int pa = find (a), pb = find (b); d[pa] = Size[pb]; Size[pb] += Size[pa]; p[pa] = pb; }else { int pa = find (a), pb = find (b); if (pa != pb){ cout << "-1" << endl; }else { if (a != b) cout << abs (d[a] - d[b]) - 1 << endl; else cout << 0 << endl; } } } }
239. 奇偶游戏 题目链接: 239. 奇偶游戏
题解思路: 总思想:前缀和
前缀和思想,如果是[l, r]
内有奇数个1的话那么必有S[r] - S[l-1]为奇数,也即是S[r]、S[l - 1]
奇偶性不同
同理如果是[l, r]
内有偶数个1的话那么必有S[r] - S[l-1]为偶数,也即是S[r]、S[l - 1]
奇偶性相同
方法一(扩展域) 枚举思想:
假设这个节点a是偶,并添加一个节点是a+M是奇
奇偶性相同的放在同一个集合中去
如果一组数据中,类型是偶数,就判断 a与b+M在不在同一个集合中
在同一个集合中说明矛盾
不在同一个集合中说明不矛盾
如果一组数据中,类型是奇数,就判断a与b是否在同一个集合中
在同一个集合中说明,矛盾
不在同一个集合中说明不矛盾
方法二(带边权) 维护一个带边权的并查集
d[x]表示x与p[x]的关系
x与y是同类
p[x] = p[y]
d[x] ^ d[y]是0无矛盾,d[x]^d[y]是1有矛盾
p[x] != p[y]
xy异类
p[x] = p[y]
d[x] ^ d[y]是0有矛盾,d[x]^d[y]是1无矛盾
p[x] != p[y]
d[p[x]] = d[x] ^ d[y] ^ 1
综上所述,就相当于代边权一个路径d表示x与根节点的关系,在find函数中需要对d进行更新类似于238题
题目代码: 方法一 (扩展域) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <bits/stdc++.h> using namespace std;const int N = 41000 , M = N / 2 ;int p[N], n, m;unordered_map<int , int > mp; int find (int x) { if (p[x] != x)p[x] = find (p[x]); return p[x]; } int get (int x) { if (mp.count (x) == 0 ) mp[x] = ++n; return mp[x]; } int main () { cin >> n >> m; n = 0 ; for (int i = 0 ; i < N; i++) p[i] = i; int res = m; for (int i = 1 ; i <= m; i++){ string t; int a, b; cin >> a >> b >> t; a = get (a - 1 ), b = get (b); if (t == "even" ){ if (find (a) == find (b + M)){ res = i - 1 ; break ; } p[find (a)] = find (b); p[find (a + M)] = find (b + M); }else { if (find (a) == find (b)){ res = i - 1 ; break ; } p[find (a)] = find (b + M); p[find (a + M)] = find (b); } } cout << res << endl; return 0 ; }
方法二(带边权) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <bits/stdc++.h> using namespace std;const int N = 41000 , M = N / 2 ;int p[N], d[N], n, m;unordered_map<int , int > mp; int find (int x) { if (p[x] != x){ int root = find (p[x]); d[x] += d[p[x]] % 2 ; p[x] = root; } return p[x]; } int get (int x) { if (mp.count (x) == 0 ) mp[x] = ++n; return mp[x]; } int main () { cin >> n >> m; n = 0 ; for (int i = 0 ; i < N; i++) p[i] = i; int res = m; for (int i = 1 ; i <= m; i++){ string t; int a, b; cin >> a >> b >> t; a = get (a - 1 ), b = get (b); if (t == "even" ){ int pa = find (a), pb = find (b); if (pa == pb){ if ((d[a] ^ d[b]) % 2 ){ res = i - 1 ; break ; } } else { p[pa] = pb; d[pa] = d[a] ^ d[b]; } }else { int pa = find (a), pb = find (b); if (pa == pb){ if ((d[a] ^ d[b]) % 2 == 0 ){ res = i - 1 ; break ; } } else { p[pa] = pb; d[pa] = d[a] ^ d[b] ^ 1 ; } } } cout << res << endl; return 0 ; }