uva11987 Almost Union-Find(可删除元素的并查集)

【题目大意】

初始给定n个集合:{1},{2},…,{n},要求支持三种操作:

"1 p q":若p,q不在同一集合,将它们所在的集合合并成一个

"2 p q":若p,q不在同一集合,将元素p移动到q所在的集合

"3 p"   :询问p所在集合的元素个数及元素和

总共m个操作,1<=n,m<=10^5


【题解】

维护sum[x],cnt[x],表示以x为father的集合的 元素和及元素个数,就可以解决操作1和3

对于操作2,若直接把p的爸爸变为father(q),就会导致p的孩子所在的集合也变为father(q)

所以这里不能直接对p做修改。

要先移出p这个元素,可以考虑 先“使p对于原集合无效”,即删除father(p)上的对应信息, 然后新建一个元素使它与p等效,并记录这个与p等效的新元素的编号id[p],以后再使用p时用id[p]代替就行了

使这个id[p]成为一个单独集合并完善相关信息,p这个元素就算成功被移出了

再把这个单独集合与q所在的集合合并就行了


【代码】

#include<stdio.h>
#include<stdlib.h>
typedef long long LL;
LL sum[200005]={0};
int fa[200005]={0},id[200005]={0},cnt[200005]={0};
int n;
int father(int x)
{
	if(x!=fa[x]) fa[x]=father(fa[x]);
	return fa[x];
}
void hb(int x,int y)
{
	int fx=father(id[x]),fy=father(id[y]);
	fa[fx]=fy;
	sum[fy]+=sum[fx];
	cnt[fy]+=cnt[fx];
}
void get(int x)
{
	int fx=father(id[x]);
	cnt[fx]--;
	sum[fx]-=(LL)x;
	id[x]=++n;
	fa[id[x]]=id[x];
	cnt[id[x]]=1;//不能写成"cnt[id[x]]++",因为未对新建元素初始化 
	sum[id[x]]=(LL)x;
}
int main()
{
	int m,i,op,p,q;
	while(scanf("%d%d",&n,&m)==2)
	{
		for(i=1;i<=n;i++)
		{
			fa[i]=id[i]=i;
			sum[i]=(LL)i;
			cnt[i]=1;
		}
		for(;m>0;m--)
		{
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d%d",&p,&q);
				if(father(id[p])!=father(id[q])) hb(p,q);
			}
			if(op==2)
			{
				scanf("%d%d",&p,&q);
				if(father(id[p])!=father(id[q]))
				{
					get(p);
					hb(p,q);
				}
			}
			if(op==3)
			{
				scanf("%d",&p);
				printf("%d %lld\n",cnt[father(id[p])],sum[father(id[p])]);
			}
		}
	}
	return 0;
}


你可能感兴趣的:(并查集,uva)