Almost Union-Find UVA 11987

AC通道 :https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3138

[分析]

操作1与操作3显然是简单的加权并查集,而操作2只需要将并查集添上一个移动元素的功能就可以了。

当我们将元素A移动到集合B中时,显然不能直接移动A,否则会将元素A在其原集合中的儿子元素也跟着移动。

这时,我们可以新增一个元素用来代替A,原集合中的A可以只起一个转移的作用,将那个新增的元素与集合B合并即可。

注意:因为这是加权并查集,所以在移动的时候要注意适当增减原集合与新集合的权值。

具体实现方法见代码:

#include <iostream>
#include <cstdio>

using namespace std;

int n,m,cnt;
int t[300010];//集合中的元素个数
int s[300010];//集合中的权值之和
int num[300010];//新增的元素的权值
int fa[300010];//father
int nxt[300010];//保存原元素对应的新增的元素

int find(int x){
	int tmp=x,pre;
	while(tmp!=fa[tmp])tmp=fa[tmp];
	while(x!=tmp){
		pre=fa[x];
		fa[x]=tmp;
		x=pre;
	}
	return tmp;
}

void merge(int x,int y){
	int fx=find(x),fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
		t[fy]+=t[fx];
		s[fy]+=s[fx]; 
	}
}

void move(int x,int y,int real){
	int fx=find(x),fy=find(y);
	if(fx==fy)return;
	cnt++;
	fa[cnt]=cnt;
	s[fx]-=num[x];
	t[fx]--;
	s[cnt]=num[x];
	num[cnt]=num[x];
	t[cnt]=1;
	nxt[real]=cnt;
	merge(cnt,fy); 
}

void print(int x){
	int fx=find(x);
	printf("%d %d\n",t[fx],s[fx]); 
}

void PENGYIHAO_WORK(){
	for(int i=1;i<=m;i++){
		int a,b,c;
		scanf("%d%d",&a,&b);
		if(a==1){scanf("%d",&c);merge(nxt[b],nxt[c]);}
		else if(a==2){scanf("%d",&c);move(nxt[b],nxt[c],b);}
		else if(a==3){print(nxt[b]);}
	}
}

int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=1;i<=n;i++)fa[i]=i,s[i]=i,t[i]=1,num[i]=i,nxt[i]=i;
		cnt=n;
		PENGYIHAO_WORK();
	}
	return 0;
}


你可能感兴趣的:(编程)