Almost Union-Find(并查集的删除操作)

题目

I hope you know the beautiful Union-Find structure. In this problem, you’re to implement something
similar, but not identical.
The data structure you need to write is also a collection of disjoint sets, supporting 3 operations:
1 p q Union the sets containing p and q. If p and q are already in the same set,
ignore this command.
2 p q Move p to the set containing q. If p and q are already in the same set,
ignore this command.
3 p Return the number of elements and the sum of elements in the set containing p.
Initially, the collection contains n sets: {1}, {2}, {3}, . . . , {n}.
Input
There are several test cases. Each test case begins with a line containing two integers n and m
(1 ≤ n, m ≤ 100, 000), the number of integers, and the number of commands. Each of the next m lines
contains a command. For every operation, 1 ≤ p, q ≤ n. The input is terminated by end-of-file (EOF).
Output
For each type-3 command, output 2 integers: the number of elements and the sum of elements.
Explanation
Initially: {1}, {2}, {3}, {4}, {5}
Collection after operation 1 1 2: {1,2}, {3}, {4}, {5}
Collection after operation 2 3 4: {1,2}, {3,4}, {5} (we omit the empty set that is produced when
taking out 3 from {3})
Collection after operation 1 3 5: {1,2}, {3,4,5}
Collection after operation 2 4 1: {1,2,4}, {3,5}

输入

5 7
1 1 2
2 3 4
1 3 5
3 4
2 4 1
3 4
3 3

输出

3 12
3 7
2 8

题意

输入 1 a b 将a b所在的集合合并
输入 2 a b 将a 移到b所在的集合里
输入 3 a 输出a所在的集合中 元素的个数 元素的和

思路

我们知道 并查集最终形成的是一个树,树的结构比较复杂
当我们想要把一个元素从节点中取出来时,如果直接改变他的根节点,树就会发生改变,而这不是我们想要的结果

比如 {1,2,3} ,{4}
现在我们要将3和4合并到一个集合中 此时 f[3]=2 ,f[4]=4
如果我们直接令f[3]=4 那么1 和2 的根节点也将发生变化,所以我们不能这样做。所以我们要开一个新的数组 来储存我们每个节点的虚根 id[]数组
当我们要把3节点从第一个集合中取出来时 ,我们令 id[3]=5 而f[3]不变,这样数的结构就不会发生变化了,再进行接下来的操作时,我们对id[]数组进行操作就可以了

代码

#include<stdio.h>
#include<string.h>
int f[1010001],id[1001010],sum[1001000],ans[1000100],n;
void init(int n)
{
	for(int i=1; i<=n; i++)
	{
		f[i]=i;
		id[i]=i;
		sum[i]=i;
		ans[i]=1;
	}
}
void pp(int a)
{
	int k=++n;
	id[a]=k;
	f[k]=k;
	sum[k]=a;
	ans[k]=1;
}
int getf(int u)
{
	if(f[u]==u)
		return u;
	else
	{
		int x=getf(f[u]);
		f[u]=x;
		return f[u];
	}
}
void merge(int u,int v)
{
	int xx=getf(id[u]);
	int yy=getf(id[v]);
	if(xx!=yy)
	{
		f[xx]=yy;
		sum[yy]+=sum[xx];
		ans[yy]+=ans[xx];
	}
}
int main()
{
	int i,j,k,m,t,a,b;
	while(~scanf("%d%d",&n,&t))
	{
		init(n);
		while(t--)
		{
			scanf("%d",&k);
			if(k==1)
			{
				scanf("%d%d",&a,&b);
				if(getf(id[a])==getf(id[b]))
					continue;
				merge(a,b);
			}
			if(k==2)
			{
				scanf("%d%d",&a,&b);//把a移到b中
				int x=getf(id[a]);
				int y=getf(id[b]);
				if(x!=y)
				{
					ans[x]--;
					sum[x]=sum[x]-a;//在这里将它原来所在的根节点的两个数组分别减去相应//的值
					pp(a);
					merge(a,b);//再合并即可
				}
			}
			if(k==3)
			{
				scanf("%d",&a);
				printf("%d %d\n",ans[getf(id[a])],sum[getf(id[a])]);
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值