并查集
支持的操作 - 将两个集合合并 - 询问两个元素是否在一个集合中
基本原理:
- 每个集合用一颗树来表示
- 树根的编号就是整个集合的编号
- 每个节点存储他的父节点,p[x]
表示他的父节点
问题
1. 如何判断树根:if(p[x] == x)
2. 如何求x集合的编号:while(p[x] != x) x = p[x]
3. 如何合并两个集合:p[x]
是x的集合编号, p[y]
是y的集合编号,令p[x] = y
查询操作
问题2的优化:路径压缩 优化方法:
合并集合
#include <iostream>
using namespace std;
const int N = 100010;
int q[N];
int find(int x){
if(q[x] != x) q[x] = find(q[x]);
return q[x];
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;i++){ //初始化
q[i] = i;
}
char op;
int a, b;
while(m--){
cin >> op;
switch(op){
case 'M':
scanf("%d%d",&a,&b);
q[find(a)] = find(b);
break;
case 'Q':
scanf("%d%d",&a,&b);
if(find(a) == find(b)) printf("Yes\n");
else printf("No\n");
break;
}
}
return 0;
}
连通块中点的数量
#include <iostream>
using namespace std;
const int N = 100010;
int q[N], sz[N];
int find(int x){
if(q[x] != x) {
q[x] = find(q[x]);
}
return q[x];
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;i++){ //初始化
q[i] = i;
sz[i] = 1;
}
char op[4];
int a, b;
while(m--){
scanf("%s",op);
if(op[0] == 'Q'){
if(op[1] == '1'){
scanf("%d%d", &a, &b);
if(find(a) == find(b)){
printf("Yes\n");
}
else{
printf("No\n");
}
}
else{
scanf("%d", &a);
printf("%d\n",sz[find(a)]);
}
}
else{
scanf("%d%d",&a,&b);
if(find(a) == find(b)) continue;
sz[find(b)] += sz[find(a)];
q[find(a)] = find(b);
}
}
return 0;
}