P3367 并查集 模板题
P3367 并查集 模板题
📌 问题描述
题目
现在有 \(n\) 个元素,分为若干个集合。需要支持两种操作:
- 合并操作 (\(z=1\)):将 \(x\) 和 \(y\) 所在的集合合并为一个集合
- 查询操作 (\(z=2\)):查询 \(x\) 和 \(y\) 是否在同一个集合中
输入格式
- 第一行:\(n, m\)(\(n\) 个元素,\(m\) 次操作)
- 接下来 \(m\) 行:每行三个整数 \(z, x, y\)
- 若 \(z=1\):合并 \(x, y\) 所在集合
- 若 \(z=2\):查询 \(x, y\) 是否同属一个集合
输出格式
- 对每个查询操作(\(z=2\)),输出
Y或N
约束条件
- \(1 \leq n \leq 10^5\)
- \(1 \leq m \leq 10^5\)
- \(1 \leq x, y \leq n\)
💡 核心思路
每个点都是一颗树的节点,
相同集合的点的根相同,
指向同一个数
并查集 = 树结构 + 找根(find) + 合并(union) + 路径压缩优化
📋 模板步骤
初始化树
rep(i,1,n+1) fa[i]=i;//每个节点的fa都是自己
每个循环输入x,y
先找到他们各自的根
x=find(x),y=find(y);
如果合并,把y的根节点指向x的根节点,
x=fa[y];
否则判断两者的根是否相同
找根节点find函数实现
## 优化点
使用递归可将沿路的的节点的父节点都指向根节点,
使树的深度接近常数级,提升后续查询效率
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
代码实现
完整代码
const int N=2e5+5;
int a[N];
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void solve(){
int n,m;
cin>>n>>m;
rep(i,1,n+1) fa[i]=i;
int x,y,z;
while(m--){
cin>>z;
x=find(x);
y=find(y);
if(z==1){
x=fa[y];
}
else {
if(x==y) cout<<"Y\n";
else cout<<"N\n";
}
}
}
📊 复杂度分析
| 指标 | 值 | 说明 |
|---|---|---|
| 总时间 | \(O(m \alpha(n))\) | m次操作 |
| 空间复杂度 | \(O(n)\) | fa数组 |
复杂度说明
- 没有路径压缩时:find 时间复杂度为 \(O(n)\),总体为 \(O(mn)\),容易超时
- 使用路径压缩后:单次操作时间复杂度降低到 \(O(\alpha(n)) \approx O(1)\)
- 反阿克曼函数 \(\alpha(n)\) 增长极慢,\(\alpha(10^5) = 4\),实际上几乎是常数
🎯 总结
- 结构本质:并查集用树的结构表示集合关系,根节点唯一标识一个集合
- 路径压缩:使用递归替换实现,大幅优化查询效率

浙公网安备 33010602011771号