问题 A: 盟国
时间限制: 3 Sec 内存限制: 128 MB
提交: 441 解决: 99
[提交][状态][讨论版]
题目描述
世界上存在着N个国家,简单起见,编号从0~N-1,假如a国和b国是盟国,b国和c国是盟国,那么a国和c国也是盟国。另外每个国家都有权宣布退盟(注意,退盟后还可以再结盟)。
定义下面两个操作:
“M X Y” :X国和Y国结盟 (如果X与Z结盟,Y与Z结盟,那么X与Y也自动结盟).
“S X” :X国宣布退盟 (如果X与Z结盟,Y与Z结盟,Z退盟,那么X与Y还是联盟).
输入
多组case。
每组case输入一个N和M (1 ≤ N ≤ 100000 , 1 ≤ M ≤ 1000000),N是国家数,M是操作数。
接下来输入M行操作
当N=0,M=0时,结束输入
输出
对每组case输出最终有多少个联盟(如果一个国家不与任何国家联盟,它也算一个独立的联盟),格式见样例。
样例输入
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
样例输出
Case #1: 3 Case #2: 2
提示
带删除并查集
心得:输入M表示要建立x和y之间的联系,而输入s表示删除点x。
如果要删除,就要特殊处理,需要一个“找假爹”的过程。
1、让每一个节点都有一个容器,就是把每个节点放到一个盒子里,每次寻找只要找到相应的盒子就行了
2、删除就是将它放到另外一个盒子里。
3、注意:操作节点数越多,对盒子数量的需求越大,所以要开的空间也就越大(数组尽量开大)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5000500;
int a[maxn],b[maxn],idx;
char s[50];
int f(int x)
{
if(x==a[x]) return a[x];
else
{
a[x]=f(a[x]);
return a[x];
}
}
void Merge(int x,int y)
{
int t1=f(x),t2=f(y);
if(t1!=t2)
{
a[t2]=t1;
}
}
void del(int x)
{
a[x]=idx++;
}
int main(void)
{
int n,m,i,j,x,y,pt=1;
while(~scanf("%d %d",&n,&m))
{
if(n==0&&m==0) break;
idx=n+n;
memset(b,0,sizeof(b));
for(i=0;i<n;i++) a[i]=i+n;
for(i=n;i<=n+n+m;i++) a[i]=i;
for(i=0;i<m;i++)
{
scanf("%s",s);
if(s[0]=='M')
{
scanf("%d %d",&x,&y);
Merge(x,y);
}
else if(s[0]=='S')
{
scanf("%d",&x);
del(x);
}
}
int ans=0,tp;
for(i=0;i<n;i++)
{
tp=f(i);
if(b[tp]==0)
{
ans++;
b[tp]++;
}
}
printf("Case #%d: %d\n",pt++,ans);
}
return 0;
}
参考文章:https://blog.csdn.net/u013491262/article/details/21883907