在并查集中,我们一般做的操作是,把一个集合加入到另一个集合中,从而在查询时,可以高效的查询两个元素的关系。但是有的时候,我们希望可以进行删除操作,就是把一个
点,从一个集合中删除,但是并不影响跟这个点有关系的其他点之间的关系。如图:
当我们删除2号节点后,2号节点成为一个独立的集合,但是,1,3,4号点仍然在一个集合中,如图:
显然,常规的并查集并不能实现这一操作,因此,我们需要另一种写法。常规并查集的初始化的方法是把每个节点的父节点初始化成自己,fa[u]==u,u这个节点就是整个集合的祖宗,此时,u点就有了两重身份:既是一个节点的名字,也是这个集合祖宗的名字,而删除操作的关键就是只改变这一个点的祖宗,而不能改变这个点所在集合的祖宗。因此要把每个节点名字的两重身份分离开,u只表示这个节点的名字,然后给他找一个祖宗(建立虚点),而且,u永远不会是u的祖宗。先给每一个节点建立一个父节点。
void pre(){
index=n;
for(int i=0;i<n;i++){
fa[i]=index++;
}
for(int i=n;i<n*2;i++){
fa[i]=i;
}
}
0-n-1表示节点的名字,n-n*2-1表示父节点,t点的父节点是t+n号节点。index用于在删除时不断往后扩展新的节点,例如在删除了2号节点后,2号节点是 一个独立的集合,因此它的父节点不能是原来的任何一个节点中,因此fa[2]=index++;
void del(int u){
fa[u]=index;
fa[index]=index++;
}
这样做,其他原来跟2同一祖宗的节点的祖宗并没有改变,他们还是连在2号节点原来所连的虚点上,从而实现了删除操作。
例题: 两种操作:M x y将x,y并入一个集合中,S x删除x点,x点成为一个独立的集合,最后求一共有多少个集合。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int readin(){
int yi=0;
char c=getchar();
while(!isdigit(c)){
c=getchar();
}
for(;isdigit(c);c=getchar()){
yi=yi*10+c-'0';
}
return yi;
}
int n,m,fa[1000010],index,dl;
void pre(){
index=n;
for(int i=0;i<n;i++){
fa[i]=index++;
}
for(int i=n;i<n*2;i++){
fa[i]=i;
}
}
int find(int u){
if(u==fa[u]) return u;
return fa[u]=find(fa[u]);
}
void del(int u){
fa[u]=index;
fa[index]=index++;
}
void end(){
int sumn=1;
for(int i=0;i<n;i++){
int xi=find(i);
}
sort(fa,fa+n);
for(int i=1;i<n;i++){
if(fa[i]!=fa[i-1]) sumn++;
}
cout<<"Case #"<<dl<<": ";
cout<<sumn<<endl;
}
void work(){
char ci;
int xi,yi;
for(int i=1;i<=m;i++){
cin>>ci;
if(ci=='M'){
xi=readin();
yi=readin();
fa[find(xi)]=find(yi);
}
else{
xi=readin();
del(xi);
}
}
}
int main(){
while(true){
dl++;
n=readin();
m=readin();
if(!n) return 0;
pre();
work();
end();
}
}