hdoj 2473 并查集

                                               充数,充数……

大意是说有一堆垃圾邮件要处理,这些邮件均有一定的属性,有些邮件属性相同,有些不同,相同的放到同一堆里 。给出两种操作,一种是M A B 告诉你AB具有相同的属性,可归为一类;一种是S A,告诉你把垃圾邮件A删除掉。 问最后有多少堆邮件(注意邮件编号从0开始)。牵涉到并查集节点的删除,怎么处理删除的结点呢。删除的结点成为 独立的结点,但最后计数堆的数目时由于其已经删除,不能计入堆数;同时断开与他具有相同属性的结点的连边。 这里做个映射数组assist,初始映射指向自身,删除一个节点就将此节点的映射改变为沿着数组末尾拓展(依次为 n,n+1,n+2……),相当于引入新的独立节点--跟其他点没有任何关系。若下面输入仍然有这个节点的话就处理它的 映射即可,那么那个所谓的新增的结点就跟其余节点联系了起来。统计的时候只统计映射节点,就可以避开删除节 点了。统计时借助set,可避免额外开bool数组 #include<iostream> #include<cstdio> #include<cstdlib> #include<set> #include<cstring> using namespace std; const int sup=1100005; char pz; set <int> MM; int root[sup],assist[sup];//assist是辅助数组,将点与点之间映射起来 int label; int m,n; bool used[sup]; int Find(int x) { int i; for(i=x;-1!=root[i];i=root[i]); while(root[x]!=i) { x=root[x]; root[x]=i; } return i; } void Union(int a,int b) { int ta=Find(a); int tb=Find(b); if(ta!=tb) root[ta]=tb; } int main() { int i,a,b; int pzjay=0; int ans; while(scanf("%d%d",&n,&m),m||n) { memset(used,0,sizeof(used)); MM.clear(); ans=0; label=n;//辅助数组从n处往外拓展,注意邮件编号从0开始 for(i=0;i<n;++i) { root[i]=-1; assist[i]=i;//辅助数组初始指向自己 } while(m--) { getchar();//吃掉回车 pz=getchar(); if(pz=='M') { scanf("%d%d",&a,&b); a=assist[a]; b=assist[b];//这样若重新输入已删除的点就会跳到label处处理 if(Find(a)!=Find(b)) Union(a,b); } else { scanf("%d",&a); assist[a]=label; root[label]=-1; ++label; } } for(i=0;i<n;++i) MM.insert(Find(assist[i])); printf("Case #%d: %d/n",++pzjay,MM.size());//对于某些无关紧要的变量,还是适当防伪标记下的好 } return 0; } PS:输出case从1开始,偶从0开始,然后WA的天昏地暗,对代码大刀阔斧的胡乱整修。 可鉴可鉴:码子调累了,可以去找马子调调情先

你可能感兴趣的:(hdoj 2473 并查集)