Problem 2155 盟国Accept: 229 Submit: 804
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
Sample Output
思路:
如果在学完并查集并且专心刷过专题的同学们对这种题肯定是不陌生的。
我们如果直接暴力将宣布退出联盟的国家拆除的话,会改变原来树的形状,使得原本属于同一集合的点,find(i)不相等。
所以我们只要能够保留原来树的形状即可。
那么对于退出联盟的国家,我们新建立一个点,作为这个点的当前节点。我们不再去使用之前的这个国家所用的编号,而是用当前新建立出来这个点作为之后操作当前国家使用的编号。
我们维护一个数组now【i】表示当前节点i所代表的编号。
那么这样就鞥能够保留原来树的形状并且不会影响之后的操作了。
另外注意一点,在查询结果的时候,我们要注意因为有点可能原来是一个祖先节点,所以我们并不能统计f【now【i】】==now【i】(find(now【i】)==now【i】)的点的数量。而是我们要统计祖先的种类数。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
int f[1200500];
int now[1200500];
int vis[1200500];
int find(int a)
{
int r=a;
while(f[r]!=r)
r=f[r];
int i=a;
int j;
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
int merge(int a,int b)
{
int A,B;
A=find(a);
B=find(b);
if(A!=B)
{
f[B]=A;
return 1;
}
return 0;
}
int main()
{
int n,m;
int kase=0;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)break;
int pos=n;
for(int i=0;i<n;i++)f[i]=i,now[i]=i;
while(m--)
{
char str[15];
scanf("%s",str);
if(str[0]=='M')
{
int x,y;
scanf("%d%d",&x,&y);
merge(now[x],now[y]);
}
else
{
int x;
scanf("%d",&x);
now[x]=pos;
f[pos]=pos;
pos++;
}
}
int output=0;
map<int ,int >s;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{
vis[find(now[i])]++;
if(vis[find(now[i])]==1)output++;
}
printf("Case #%d: %d\n",++kase,output);
}
}

532

被折叠的 条评论
为什么被折叠?



