Problem 2155 盟国
Accept: 75 Submit: 235
Time Limit: 5000 mSec Memory Limit : 32768 KB
Problem Description
世界上存在着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
设一个real数组,记录点的真实位置。初始化时跟pre数组一样都等于i本身,删除的时候把原来所在的位置变为一个新的位置,原集合也不受影响。便于理解,模拟一下第一组测试样例:
i real[i] find(real[i]) vis ans
0 0 3 0 1
1 5 3 1 1
2 2 3 1 1
3 6 6 0 2
4 4 4 0 3
进行find查找的时候,直接用while的方法会超时,用路径压缩的方法就AC。
<pre code_snippet_id="271877" snippet_file_name="blog_20140403_1_1710727" name="code" class="cpp">//812 ms 17816KB
#include<stdio.h>
#include<string.h>
#define M 1000000<<1
int pre[M],real[M];
bool vis[M];
int n,m;
/* 超时
int find(int x)
{
while(x!=pre[x])
x=pre[x];
return x;
}
*/
int find(int x)//路径压缩
{
int root=x;
while(root!=pre[root])
{
root=pre[root];
}
while(x!=root)
{
int temp=pre[x];
pre[x]=root;
x=temp;
}
return root;
}
/*
int find(int cur)路径压缩
{
return pre[cur]==cur?cur:pre[cur]=find(pre[cur]);
}
*/
int main()
{
int cas=1;
while(scanf("%d%d",&n,&m),n|m)
{
for(int i=0; i<n; i++)
pre[i]=real[i]=i;
memset(vis,0,sizeof(vis));
int a,b,k=n;
while(m--)
{
getchar();
char s;
scanf("%c",&s);
if(s=='M')
{
scanf("%d%d",&a,&b);
int x=find(real[a]);
int y=find(real[b]);
if(x!=y)pre[x]=y;
}
else
{
scanf("%d",&a);
pre[k]=k;
real[a]=k++;//记录新的位置
}
}
int ans=0;
for(int i=0; i<n; i++)
{
int x=find(real[i]);
if(!vis[x])
{
vis[x]=1;
ans++;
}
}
printf("Case #%d: %d\n",cas++,ans);
}
return 0;
}