有 n 棵树,编号 0 到 n - 1 , 本来以为它们是 n 种不同的树,但是我们陆陆续续的得到了 m 条消息,发现之前统计错了。
消息有如下两种格式:
- ”M a b" ,编号 a 和 编号 b 的树划分为一种树 ;
- "S a", 将编号为 a 的树从它所属的种类里拿出来,成为一个新的种类;
请问最终有多少种树 ?
Input
输入文件中有多个测试用例。
每个测试用例第一行是两个整数 n 和 m(1 ≤ n ≤ 1e5,1 ≤ M ≤ 1e6),随后是 m 行,每行是上述两种格式之一。
n = 0 和 m = 0 的时候结束。
Output
对于每个测试用例,打印一个整数 —— 树的种类。具体格式参照样例。
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
主要是删除的时候肯定不能像以前一样删除
合并操作就是找到找到两个节点的父亲,修改父亲,如果删除就是将该点的父亲重新设置成自己,但是这是不行的,比如1,2,3的父亲都是1,现在删除1,1的父亲还是1,2,3也是1,集合还是1个,正确的应该是2个。
那删除节点的父亲不设成自己给新申请一个节点当做父亲,比如1,2,3的父亲都是1,在一个集合,现在删除1,申请了4当做1的父亲,2,3父亲都是1,然后Find(2)找2的父亲 2的父亲是1,但是1的父亲是4,所以给2的父亲更新成了4,3同理,所以还不行。
正确的方法是每一个点都设立一个虚拟父亲比如1,2,3的父亲分别是4,5,6,现在合并1,2,3都在一个集合,那他们的父亲都是4,现在删除1,那就给1重新申请一个节点7
现在2,3的父亲是4,1的父亲是7,删除成功。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2e6+100;
int pre[maxn];
int vis[maxn];
int n,m;
int kase=0;
int id;
int find(int x){
if(pre[x]!=x){
pre[x]=find(pre[x]);
}
return pre[x];
}
void marge(int x,int y){
int t1=find(x);
int t2=find(y);
if(t1!=t2){
pre[t1]=t2;
}
}
void del(int x){
pre[x]=id++;
}
void inint(){
for(int i=0;i<n;i++){
pre[i]=i+n;
vis[i]=0;
}
for(int i=n;i<=n+n+m;i++){//最多m次操作
pre[i]=i;
vis[i]=0;
}
id=n+n;
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(n+m==0){
break;
}
inint();
int x,y;
char a[maxn];
for(int i=1;i<=m;i++){
scanf("%s",a);
if(a[0]=='M'){
scanf("%d%d",&x,&y);
marge(x,y);
}
else{
scanf("%d",&x);
del(x);
}
}
int ans=0;
for(int i=0;i<n;i++){
int t=find(i);
if(!vis[t]){
ans++;
vis[t]=1;
}
}
printf("Case #%d: %d
",++kase,ans);
}
}