AcWing算法提高课 并查集
并查集基本操作:
1、合并两个集合
2、查询集合的代表元素(祖先节点)
优化方式:
1、路径压缩(合并查询复杂度都是logn)
2、按秩合并(每次合并将深度较小的合并到深度较大的,logn)
3、1+2可以达到O(α(n))≈O(1),α():反Ackermann函数
扩展:
1、记录集合大小
2、记录到根节点的距离
通过到根节点的距离,表示与根节点的关系,从而达到对集合中的点进行分类
例题:食物链
3、拓展域并查集
拓展域中的点表示的不是变量而是变量的一个状态(取值)
模板:
基本并查集:

int p[200010]; int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int merge(int x,int y) { int px=find(x); int py=find(y); p[px]=p[y]; }
维护集合中全部物体的,价值\体积\数值的和,的并查集(和维护大小同理,绑定到根节点)

int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void merge(int x, int y) { int px = find(x); int py = find(y); if (px == py) return; val[py] += val[px]; v[py] += v[px]; p[px] = py; }
维护距离的并查集(通过d和sz数组记录距离和集合大小,此模板合并时接到尾部,可以直接设置距离为sz的对应值)
(同时注意在find时要先存储原始的p[a],d[x]记录的是x到p[x]的距离,到祖先的距离会在find中更新):

int p[30010]; int d[30010]; int sz[30010]; int find(int a) { if (p[a] != a) { int opa = p[a];//注意下一句会更新p[a],所以要先把原始的p[a]存起来 p[a] = find(p[a]); d[a] = d[opa] + d[a];//在find时会查找祖先,所以可以更新到祖先距离 } return p[a]; } void merge(int a, int b) { int pa = find(a), pb = find(b); if (pa == pb) return; p[pa] = pb;//把a的祖先合并到b的祖先 d[pa] = sz[pb];//距离设置为b的祖先的集合的大小,即把pa接到b所在集合的后面 sz[pb] += sz[pa];//更新集合大小 }
例题:
1、AcWing 1250. 格子游戏:
(并查集对一维坐标比较友好,可以把二维坐标转化为一维,一般从0开始的坐标比较方便)
2、求连通块可以用并查集
3、237. 程序自动分析
此题的数据范围较大但数据量1e5,故需要使用哈希表或离散化方式。
由于数据量问题,哈希表运行比较慢可能超时,所以需要离散化
离散化的一种(保序的)方式是,放入数组,排序,判重,查找时二分。
哈希表代码:

#include <iostream> using namespace std; typedef long long LL; #include<bits/stdc++.h> #include<unordered_set> unordered_map<int, int> p; int a[100010]; int b[100010]; int op[100010]; LL find(LL x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void merge(LL x, LL y) { LL px = find(x); LL py = find(y); p[px] = py; } void YD() { int n; cin >> n; p.clear(); for (int i = 1; i <= n; i++) { cin >> a[i] >> b[i]; cin >> op[i]; p[a[i]] = a[i]; p[b[i]] = b[i]; } for (int i = 1; i <= n; i++) { if (op[i]) merge(a[i], b[i]); } for (int i = 1; i <= n; i++) { if (!op[i]) { LL pa = find(a[i]); LL pb = find(b[i]); if (pa == pb) { cout << "NO" << endl; return; } } } cout << "YES" << endl; } int main() { int t=1; cin >> t; while (t--) { YD(); } }
离散化代码:
使用新数组:

#include <iostream> using namespace std; typedef long long LL; #include<bits/stdc++.h> #include<unordered_set> LL p[200010]; int a[200010]; int b[200010]; int op[200010]; LL find(LL x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void merge(LL x, LL y) { LL px = find(x); LL py = find(y); p[px] = py; } void YD() { int n; cin >> n; vector<LL> nums; for (int i = 1; i <= n; i++) { cin >> a[i] >> b[i]; cin >> op[i]; nums.push_back(a[i]); nums.push_back(b[i]); } sort(nums.begin(), nums.end()); vector<LL> numm; for (int i = 0; i < nums.size(); i++) { if (i == 0 || nums[i] != nums[i - 1]) numm.push_back(nums[i]); } nums = move(numm); for (int i = 0; i < nums.size(); i++) p[i] = i; for (int i = 1; i <= n; i++) { if (op[i]) { auto index1 = lower_bound(nums.begin(), nums.end(), a[i])-nums.begin(); auto index2 = lower_bound(nums.begin(), nums.end(), b[i]) - nums.begin(); merge(index1, index2); } } for (int i = 1; i <= n; i++) { if (!op[i]) { auto index1 = lower_bound(nums.begin(), nums.end(), a[i]) - nums.begin(); auto index2 = lower_bound(nums.begin(), nums.end(), b[i]) - nums.begin(); LL pa = find(index1); LL pb = find(index2); if (pa == pb) { cout << "NO" << endl; return; } } } cout << "YES" << endl; } int main() { int t=1; cin >> t; while (t--) { YD(); } }
使用unique:

#include <iostream> using namespace std; typedef long long LL; #include<bits/stdc++.h> #include<unordered_set> LL p[200010]; int a[100010]; int b[100010]; int op[100010]; LL find(LL x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void merge(LL x, LL y) { LL px = find(x); LL py = find(y); p[px] = py; } void YD() { int n; cin >> n; vector<LL> nums; for (int i = 1; i <= n; i++) { cin >> a[i] >> b[i]; cin >> op[i]; nums.push_back(a[i]); nums.push_back(b[i]); } sort(nums.begin(), nums.end()); auto it = unique(nums.begin(), nums.end()); int nn = it - nums.begin(); for (int i = 0; i < nn; i++) p[i] = i; for (int i = 1; i <= n; i++) { if (op[i]) { auto index1 = lower_bound(nums.begin(), nums.begin()+nn, a[i])-nums.begin(); auto index2 = lower_bound(nums.begin(), nums.begin()+nn, b[i]) - nums.begin(); merge(index1, index2); } } for (int i = 1; i <= n; i++) { if (!op[i]) { auto index1 = lower_bound(nums.begin(), nums.begin()+nn, a[i]) - nums.begin(); auto index2 = lower_bound(nums.begin(), nums.begin()+nn, b[i]) - nums.begin(); LL pa = find(index1); LL pb = find(index2); if (pa == pb) { cout << "NO" << endl; return; } } } cout << "YES" << endl; } int main() { int t=1; cin >> t; while (t--) { YD(); } }
4、AcWing 239. 奇偶游戏
离散化+前缀和思想+记录距离(模2)

#include <iostream> using namespace std; typedef long long LL; #include<bits/stdc++.h> #include<unordered_set> unordered_map<int, int> mmap; int p[10010]; int d[10010]; int find(int x) { if (p[x] != x) { int opx = p[x]; p[x] = find(p[x]); d[x] = d[x] + d[opx]; d[x] %= 2; } return p[x]; } void merge(int x, int y, bool odd) { int px = find(x); int py = find(y); if (d[x] == d[y]) { if (odd) { p[px] = py; d[px] = 1; } else { p[px] = py; d[px] = 0; } } else { if (odd) { p[px] = py; d[px] = 0; } else { p[px] = py; d[px] = 1; } } } void YD() { int n; cin >> n; mmap.clear(); int index = 1; int m; cin >> m; for (int i = 1; i <= m; i++) { int x, y; string op; cin >> x >> y >> op; x--; if (!mmap.count(x)) { mmap[x] = index; p[index] = index; d[index] = 0; index++; } if (!mmap.count(y)) { mmap[y] = index; p[index] = index; d[index] = 0; index++; } int px = find(mmap[x]); int py = find(mmap[y]); if (px == py) { if (op == "even" && d[mmap[x]] != d[mmap[y]]) { cout << i - 1 << endl; return; } if (op == "odd" && d[mmap[x]] == d[mmap[y]]) { cout << i - 1 << endl; return; } } merge(mmap[x], mmap[y], op == "odd"); } cout << m << endl; } int main() { int t=1; //cin >> t; while (t--) { YD(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人