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

题目链接:https://cn.vjudge.net/contest/320014#problem/D
前提:并查集是一种用来管理元素分组情况的数据结构。(并查集是用树形结构实现的)

翻译
三种操作:
1 x y:将元素x和y所在的集合合并(可用并查集)
2 x y:将元素x移到y所在的集合
3 x:输出元素x所在集合的元素的数量和元素的总和

分析:关键为如何进行2操作,如果x为所在集合的叶节点,可以直接用并查集把x归并到y所在的集合。但如果不是叶节点,直接把x归到y所在的集合,会把x所有的儿子也全都归并到y所在的集合。对于3的输出操作,并查集的根结点在路径压缩中是不变的,可以将两个信息维护在根结点上。
问题转化为了对于2操作,如何把x节点从集合中剥离出来,又不影响集合的结构
解决方案:将每个节点都起一个编号:g[i]=i,表示节点i的编号为i,对于1操作,每次把节点所表示的编号进行并查集。对于2操作,把要去除的节点开一个新的编号,相当于这个节点进行了“分身”,真身留在原集合维护树的结构,分身出来归到y集合中。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int f[100001],g[100001];
int num[100001],sum[100001];
void init()
{
    for(int i=1; i<=n; i++) /*刚开始有n个集合*/
    {
        f[i]=g[i]=i;
        sum[i]=i;//sum记录一个集合中元素的总和,num记录一个集合中元素的个数
        num[i]=1;
    }
}
int getf(int x)
{
    if(f[x]==x)
        return x;
    else
    {
        f[x]=getf(f[x]);
        return f[x];
    }
}
void unite(int x,int y)
{
    int t1=getf(x);
    int t2=getf(y);
    if(t1!=t2)
    {
        f[t1]=t2;
        sum[t2]+=sum[t1];
        num[t2]+=num[t1];
    }
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        int temp=n;
        init();
        int p,q;
        while(m--)
        {
            int a;
            scanf("%d",&a);//a的取值为1 2 3
            if(a==1)
            {
                scanf("%d%d",&p,&q);//将元素p,q所在的集合合并
                unite(g[p],g[q]);
            }
            if(a==2)
            {
                scanf("%d%d",&p,&q);//将元素p,移动到q所在的集合
                int u=getf(g[p]),v=getf(g[q]);
                if(u!=v)
                {
                    sum[u]-=p;//把p从集合中去除
                    num[u]--;
                    temp++;
                    g[p]=temp;
                    f[temp]=temp;
                    sum[temp]=p;
                    num[temp]=1;
                    unite(g[p],g[q]);
                }
            }
            if(a==3)
            {
                scanf("%d",&p);//输出p所在集合元素的数量和总和
                int d=getf(g[p]);
                printf("%d %d\n",num[d],sum[d]);
            }
        }
    }
    return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值