hdoj 2473 Junk-Mail Filter 并查集的删除

并查集的删除

删除当前节点,准确的说是从当前节点集合中剔除当前节点,它的子节点不要动。普通的并查集在合并的时候有些节点是有字节点的,如果删除了这个节点的关系(把它的父亲设置为自己),那么当它的子节点合并的时候就找不到父亲了。解决的方法有两种:(很直接的)遍历所有的节点,findp(x),归并到根,显然不可能,或者在插入的时候,每一个节点都是叶子节点,那么删除的时候就很方便,不用没有归并到根的子节点了。//保证在删除这个节点之前,它的所有子节点已经归并到根上面了 //引入虚拟节点,当合并的时候都把节点丢给虚拟节点,这样当删除节点的时候,只要把它自己指向一个没用过的虚拟节点就可以了 //保证每一个节点都是叶子 #include<iostream> #include<string.h> using namespace std; int f[2200000]; int flag[2200000]; int kind; int findp(int x) { return x==f[x]?x:f[x]=findp(f[x]); } void unions(int x,int y) { if(findp(x)>1000000) f[findp(y)]=findp(x); else if(findp(y)>1000000) f[findp(x)]=findp(y); else if(findp(x)>1000000&&findp(y)>1000000) f[findp(x)]=findp(y); else { f[findp(x)]=kind++; f[findp(y)]=kind-1; } } void del(int x) { f[x]=kind++; } int main() { int fcount,dcount,c=1,i,j,k,res; char s; while(scanf("%d%d",&fcount,&dcount)!=EOF) { if(fcount==0&&dcount==0) return 0; memset(flag,0,sizeof(flag)); for(i=0;i<2100200;i++) f[i]=i; kind=1000001; for(i=0;i<dcount;i++) { cin>>s; if(s=='M') { scanf("%d%d",&j,&k); if(findp(j)!=findp(k)) unions(j,k); } else { scanf("%d",&j); del(j); } } res=0; for(i=0;i<fcount;i++) if(!flag[findp(i)]) { res++; flag[findp(i)]=1; } printf("Case #%d: %d/n",c++,res); } return 0; }

你可能感兴趣的:(hdoj 2473 Junk-Mail Filter 并查集的删除)