UVa-11987 Almost union-find(带删除操作的并查集)

题意:有三种操作:

1 p q:合并元素p和q所在集合

2 p q:把元素p移动到q所在集合

3 p   :输出p所在集合元素个数和该集合所有元素之和

分析:这道题考察了并查集的删除操作以及统计并查集元素和, 对于每一个集合,我们只需要考虑根节点root号位置,注意find查找到的是根节点的位置,实际根节点有可能不在这里,用cnt[root]和sum[root]来代表该集合元素个数与元素之和,并且在每一次操作时进行更新,初始时k号节点就在k号位置

下面重点讲讲删除操作,刚开始我的写法是像链表那样,把i的子节点与i的父节点相连,但是这样会超时。

我们用id[i]来代表节点i实际的位置,意思就是比如id[2]=2说明节点2在2号位置,id[2]=6说明节点2在6号位置,每次删除节点,我们可以新建一个比n大的节点序号tot,然后使id[i]=tot, 再合并id[i]和另一个集合。比如n=5,tot=6,i=2,id[i]=6  这样下一次我们再找2号的根节点时,我们找的是6号的根节点,也就是2号所属的当前集合的根节点。

这种做法无论删除的店是不是根节点都是成立的

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 100005
using namespace std;

int pa[MAXN];
long long cnt[MAXN], sum[MAXN];//分别代表集合元素个数和元素总和 
int id[MAXN];//id[i]代表i现在所处的位置,i没被删除时id[i]=i,i被删除移动后id[i]=tot>n 
int n, m;
int k, p, q;

int find(int x){ 
	return pa[x]==x? pa[x]=x:find(pa[x]);
}


int main(){
	while(~scanf("%d%d", &n, &m)){
		int tot = n;
		for(int i = 1; i<=n; i++){
			pa[i] = i;
			cnt[i] = 1;
			sum[i] = i;
			id[i] = i;
		}
		for(int i = 0; i<m; i++){
			scanf("%d", &k);
			if(k==1){
				scanf("%d%d", &p, &q);
				int root1 = find(id[p]), root2 = find(id[q]);//要找当前的位置,所以是id
				if(root1==root2) continue;
				pa[root1] = root2;
				sum[root2]+=sum[root1];
				cnt[root2]+=cnt[root1];
			}
			if(k==2){
				scanf("%d%d", &p, &q);
				int root1 = find(id[p]), root2 = find(id[q]);
				if(root1==root2) continue;
				id[p] = ++tot;//创建一个新节点,并且用新节点的值作为结点p的新位置
				pa[id[p]] = pa[id[q]];
				sum[root2]+=p;
				cnt[root2]++;
				sum[root1]-=p;
				cnt[root1]--;
			}
			if(k==3){
				scanf("%d",&p);
				int root = find(id[p]);
				printf("%lld %lld\n", cnt[root], sum[root]);
			}
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值