并查集
简介
定义
用于快速合并和查询集合的数据结构
操作
- 合并两个集合
- 查询某个点在哪个集合
优化
- 路径压缩
- 每次在查询一个节点时,直接将该节点指向集合根节点
- 按秩合并
- 将集合中元素数量少的接在元素数量多的集合中
模板
836. 合并集合
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int p[N];
// 寻找x的祖宗节点
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m;
// 初始化并查集
for(int i = 0; i < n; i ++)
{
p[i] = i;
}
while(m -- )
{
char op[2];
int a, b;
cin >> op >> a >> b;
if(op[0] == 'M')
{
// 合并两个集合
p[find(a)] = find(b);
} else {
// 查询两个数是否在同一个集合中
if(find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
应用
837. 连通块中点的数量
分析
本题中只有询问集合数量是特别引入,其他与模板相同
- 额外记录集合元素数量
- 将元素数量与根节点绑定,更新时,只需要用根节点的数量加上新加入元素的节点
代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int p[N], s[N];
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++)
{
p[i] = i;
s[i] = 1;
}
while(m -- )
{
string op;
int a, b;
cin >> op;
if(op == "Q1")
{
// 两个元素是否在一个集合中
cin >> a >> b;
if(find(a) == find(b)) puts("Yes");
else puts("No");
} else if(op == "Q2")
{
// 询问集合的元素数量
cin >> a;
printf("%d\n", s[find(a)]);
} else {
// 合并集合
cin >> a >> b;
if(find(a) == find(b)) continue;
s[find(b)] += s[find(a)];
p[find(a)] = find(b);
}
}
return 0;
}
240. 食物链
分析
本题中引入了新的元素距离,其他与模板相同
- 将元素距离根节点的距离元素节点绑定
代码
#include<iostream>
using namespace std;
const int N = 50010;
int n, k;
int p[N], d[N];
int find(int x)
{
if(x != p[x])
{
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main()
{
cin >> n >> k;
// 初始化并查集
for(int i = 1 ; i <= n ;i ++ ) p[i] = i;
int res = 0; // 表示假话的数量
while(k -- )
{
// 处理询问
int op, x, y;
cin >> op >>x >> y;
if(x > n || y > n) res ++ ;
else {
int px = find(x), py = find(y);
if(op == 1)
{
// x 与 y为同类
if(px == py && (d[x] - d[y]) % 3) res ++ ;
else if(px != py)
{
p[px] = py;
d[px] = d[y] - d[x];
}
} else {
// x吃y dx 比dy多一
if(px == py && (d[x] - d[y] - 1) % 3) res ++ ;
else if(px != py)
{
p[px] = py;
d[px] = d[y] - d[x] + 1;
}
}
}
}
cout << res << endl;
return 0;
}