0
点赞
收藏
分享

微信扫一扫

Fzu 2155 盟国(并查集删除操作)

SPEIKE 2022-06-17 阅读 44

并查集的删除
通过路径压缩后的并查集,都是每个节点直接和它的根节点相连,如果要删除某一个点的话,直接让他的father的值为自己就好了。但是也存在一个问题,并不一定所有情况的并查集都会是子节点直接指向自己,或者要删除的哪个节点正好是根节点,这样的话如果单纯改变father的值,那么其他的节点也会收到影响。
为了解决这个问题,我们可以把每个节点装进一个盒子里,正常的合并后,如果要删除某一个节点,就把这个节点拿出来,放到另一个盒子里就好了,这样也不会改变原来集合的状态。

Fzu 2155 盟国
世界上存在着N个国家,简单起见,编号从0~N-1,假如a国和b国是盟国,b国和c国是盟国,那么a国和c国也是盟国。另外每个国家都有权宣布退盟(注意,退盟后还可以再结盟)。
定义下面两个操作:
“M X Y” :X国和Y国结盟
“S X” :X国宣布退盟
Input
多组case。
每组case输入一个N和M (1 ≤ N ≤ 100000 , 1 ≤ M ≤ 1000000),N是国家数,M是操作数。
接下来输入M行操作
当N=0,M=0时,结束输入
Output
对每组case输出最终有多少个联盟,格式见样例。
Sample Input
5 6
M 0 1
M 1 2
M 1 3
S 1
M 1 2
S 3
3 1
M 1 2
0 0
Sample Output
Case #1: 3
Case #2: 2

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int father[500005], real[500005];//real[i]为i点所在的盒子
int vis[500005];//vis用于最后判断集合的个数
int n, m, all;

int find(int x)
{
if(x==father[x])
return father[x];
return father[x]=find(father[x]);
}
void join(int x, int y)
{
int fx=find(x), fy=find(y);
if (fx!=fy)
father[fy]=fx;
}

int main()
{
int t=1;
while (scanf("%d%d", &n, &m)!=EOF){
if (n==0 && m==0) break;
all=n;
memset(vis, 0, sizeof(vis));
for (int i=0; i<n; i++){
father[i]=i;//一开始每个点的老大都是自己
real[i]=i;//每个点都在自己的盒子里
}
for (int i=0; i<m; i++){
getchar();
char ch=getchar();
int a, b;
if (ch=='M'){
scanf("%d%d", &a, &b);
join(real[a], real[b]);//将两个点所在的盒子合并相连
}else{
scanf("%d", &a);
father[all]=all;//找一个0到n-1以外的盒子,并初始化
real[a]=all;//把这个要删除的点放到这个盒子里
all++;
}
}
int ans=0;
for (int i=0; i<n; i++){
int t=find(real[i]);
if (vis[t]==0){
ans++;
vis[t]=1;
}
}
printf("Case #%d: %d\n", t++, ans);
}
return 0;
}


举报

相关推荐

0 条评论