并查集
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。
(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
//实例测试,合并集合
#include<iostream>
using namespace std;
const int N = 1e5+10;
int p[N];
int find(int x){
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
int main(){
int n,m;
int x,y;
char s[2];
scanf("%d%d",&n,&m);
for(int i = 0;i<n;i++) p[i] = i;
while(m--){
scanf("%s%d%d",s,&x,&y);
if(s[0]=='M'){
p[find(x)] = find(y);
}
else{
if(find(x) != find(y)) printf("No\n");
else printf("Yes\n");
}
}
return 0;
}
//测试实例连通块中点的数量
#include<iostream>
using namespace std;
const int N = 1e5+10;
int p[N], s[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
int n = 0, m = 0;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
p[i] = i;
s[i] = 1; // 初始化为1
}
while (m--) {
char cmd[2];
int a, b;
scanf("%s", cmd);
if (cmd[0] == 'C') {
scanf("%d%d", &a, &b);
if (find(a) == find(b)) continue;
s[find(b)] += s[find(a)];
p[find(a)] = find(b);
} else if (cmd[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", s[find(a)]);
}
}
return 0;
}
//测试实例,食物链
#include<iostream>
using namespace std;
const int N = 5e4+10;
int q[N],d[N];
int find(int x){
if(q[x]!=x){
int t =q[x];
q[x] = find(q[x]);
d[x] += d[t];
}
return q[x];
}
int main(){
int n,m,res=0;
scanf("%d%d",&n,&m);
for(int i = 0;i<n;i++) q[i] = i;
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(b>n||c>n) {
res++;
continue;
}
if(a==2&&b==c){
res++;
continue;
}
int qb = find(b);
int qc = find(c);
if(a==1){
if(qb==qc&&(d[b]-d[c])%3) res++;
else if(qb!=qc){
q[qb] = qc;
d[qb] = d[c] - d[b];
}
}
else{
if(qb==qc&&(d[b]-d[c]-1)%3) res++;
else if(qb!=qc){
q[qb] = qc;
d[qb] = d[c] - d[b] +1;
}
}
}
printf("%d",res);
return 0;
}