有 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); } }