【题目】
题目传送门Junk-Mail Filter
Description
我简略地说一下题目意思吧
有多组数据,每组数据有 n 个物品,m 个操作,你要支持以下两种操作:
1、M X Y,将 X 和 Y 合并(放入同一个集合中)
2、S X,将 X 从原来的集合中删除,放入一个新的集合中
求出经过 m 次操作之后还有多少个集合
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
【分析】
这道题的话,操作 M 很好做,用并查集就行了,但是删除不太好做,听说并查集中删点的代码要写300多行
一个简单的做法是对于每个要删除的点 x,新建一个节点 y,然后将 x 指向 y,以后要用 x 的时候用 y 就可以了
emmm……是不是很简单,接下来看代码吧
【代码】
注意这些物品是从 0 开始编号的
sum[ i ] 记录的是 i 号集合中物品的个数
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000005
using namespace std;
int id[N],sum[N],father[N];
int find(int x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return father[x];
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(x==y) return;
father[y]=x;
sum[x]+=sum[y];
sum[y]=0;
}
int main()
{
int n,m,i,x,y,ans,num=0;
char c;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!n&&!m)
break;
ans=0;
num++;
for(i=0;i<=n+m;++i)
{
id[i]=i;
sum[i]=1;
father[i]=i;
}
for(i=1;i<=m;++i)
{
c=getchar();
c=getchar();
if(c=='M')
{
scanf("%d%d",&x,&y);
merge(id[x],id[y]);
}
if(c=='S')
{
scanf("%d",&x);
sum[find(id[x])]--;
id[x]=n++;
}
}
for(i=0;i<n;i++)
if(sum[i])
ans++;
printf("Case #%d: %d\n",num,ans);
}
return 0;
}