题意:给定n个点,刚进行两种操作,r如果为M将两个点合并,如果为S将一个得到点与前面已经合并的堆中孤立,问最后点有几堆;
分析:删除一个点,只是将该点独立起来,或者说将该点从所在集合中脱离,而所在集合的结构不变,若真的将该点从集合中删去,会带来很多不必要的麻烦,所以,可以反而添加一个虚拟的点代替独立出来的点,这样,用一个数组hash将每一个点都映射到一个虚拟的点上,之后在虚拟的点(hash)上面操作即可。当要独立一个点时,只需将该点映射到另一个原先不存在的点即可。
之后,统计集合个数有俩种方法,一个是根据集合含有的元素的个数,另一个则是统计根节点的个数。
代码:
#include<stdio.h>
#define M 1000005
int a[M],hash[M],ret[M];//hash为数组a的映射数组,ret记录每个堆中元素和;
int find(int tt)
{
if(tt!=a[tt])
a[tt]=find(a[tt]);
return a[tt];
}
void sort(int x,int y)
{
int zx=find(x);
int zy=find(y);
if(zx==zy)
return;
if(ret[zx]<ret[zy]) //将一个堆的元素和集中在根节点上,堆中其他点则可以ret为0;
{
a[zx]=zy;
ret[zy]+=ret[zx];
ret[zx]=0;
}
else
{
a[zy]=zx;
ret[zx]+=ret[zy];
ret[zy]=0;
}
}
int main()
{
int n,m,ss=0;
while(~scanf("%d%d",&n,&m),n||m)
{
char c;
int x,y;
int i,j,k;
for(i=0;i<M;i++)
{
a[i]=i;
hash[i]=i;
ret[i]=1;
}
while(m--)
{
getchar();
scanf("%c",&c);
if(c=='M')
{
scanf("%d%d",&x,&y);
sort(hash[x],hash[y]);//应该引用x,y对应的映射值;
}
else
{
scanf("%d",&x);
ret[find(hash[x])]--;//将x所在的堆得元素和减一;
hash[x]=n++; //将x映射到n;
}
}
int ans=0;
for(i=0;i<n;i++)
if(ret[i]>0)//判断有几个堆;
ans++;
printf("Case #%d: %d\n",++ss,ans);
}
}