前言
并查集这部分还是挺重要的,应该要熟练掌握哦!!!
一、并查集的基本概念
作用:
1、将两个集合合并
2、查询是否在一个集合内
基本原理:
每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点储存它的父节点,p[x]表示x的父节点。
问题:
1、如何判断树根:if(p[x] == x)
2、如何求x的集合编号:while(p[x] != x) x = p[x]
3、如何合并两个集合:px是x的集合编号,py是y的集合编号,p[x] = y
二、例题
1.合并集合:
#include<iostream>
using namespace std;
const int N = 1e5+10;
// p数组是做什么的?---存储每个节点的根节点
// p[x] != x代表它是一个根节点
int p[N],a,b;
// 递归---先递归到最底层,再开始回溯赋值
// find函数的作用:找到x的根节点并返回,并压缩路径
int find(int x){
if(p[x] != x) p[x] = find(p[x]); // 压缩路径,将查询x的所有长辈节点直接接在根节点上
return p[x];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i < n;i++) p[i] = i;
while(m--){
char op[2];
scanf("%s%d%d",op,&a,&b);
// 合并---让a集合直接连接到b集合的根节点下
if(*op == 'M') p[find(a)] = p[find(b)];
else{
// 查询是否是同一个集合,如果a集合和b集合具有相同的根节点,则它们是同一个集合
if(find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
2.连通块中点的数量:
//P23连通块中点的数量
//并查集
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
//p存储每个点
//size表示每一个集合的大小,每一个集合里元素的个数,就是存储根节点所拥有的子节点数
//只保证根节点的size有意义
int p[N], sizee[N];
int find(int x) {//返回祖宗节点+路径压缩
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
p[i] = i;
sizee[i] = 1;//最开始的时候每一个集合里只有自己一个点
}
while (m--) {
char op[3];
int a, b;
scanf("%s", op);
if (op[0] == 'C') {//连边,就是合并
scanf("%d%d", &a, &b);
if (find(a) == find(b))//如果a和b已经在同一个集合里了,就continue跳出
continue;
sizee[find(b)] += sizee[find(a)];//将节点数加到新根节点数上
p[find(a)] = find(b);//a的根节点的根节点等于b的根节点
} else if (op[1] == '1') {
scanf("%d%d", &a, &b);
if (find(a) == find(b))//判断是或在同一个集合里
puts("Yes");
else
puts("No");
} else {
scanf("%d", &a);
printf("%d\n", sizee[find(a)]);
}
}
return 0;
}
3、食物链:
//余1,可以吃根节点
//余2,可以被根节点吃
//余0,与根节点是同类
#include <iostream>
using namespace std;
const int N = 50010;
int n, m;
int p[N], d[N];
//p[n]是n的父节点,d[n]是n节点到根节点的距离
int find(int x) {
if (p[x] != x) { //如果当前节点x不是根节点
int t = find(p[x]);//先用t存下来x的根节点
d[x] += d[p[x]]; //把d[x]更新成x到根节点之间的距离
p[x] = t; //再把p[x]更新成x到根节点的距离
}
return p[x];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
p[i] = i;//初始化父节点是自己,d距离初始化都为0
int res = 0;//记录假话数量
while (m--) {
int t, x, y;
scanf("%d%d%d", &t, &x, &y);
if (x > n || y > n)
res++;//如果x,y超出动物总数n,就说明是假话,res++
else {
int px = find(x), py = find(y); //先找根节点
if (t == 1) { //现在x和y是同类
//x和y在同一颗树上,且x到根节点的距离和y到根节点的距离之差模3不等于0,就是假话
if (px == py && (d[x] - d[y] ) % 3)
res++;
else if (px != py) { //如果x和y不在同一颗树上
p[px] = py;//就把x和y放到同一颗树上
d[px] = d[y] - d[x];
}
} else {//t==2时,表示x吃y
//x和y在同一颗树上,x到根节点的距离应该比y到根节点的距离多1,否则是假话
if (px == py && (d[x] - d[y] - 1) % 3)
res++;
else if (px != py) { //x和y不在同一颗树上
p[px] = py;//把px更新到py树下,也就是px的父节点更新成py
d[px] = d[y] + 1 - d[x];
}
}
}
}
printf("%d\n", res);
return 0;
}
总结
以上就是我对这部分的理解,感谢大家的观看谢谢大家!!!