UVA 11987 Almost Union-Find 并查集节点删除

题目链接http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3138

题目大意:对于1-n这n个数,每个数为一个集合,现在有三种操作:第一种:1 x y  即把x 和 y所在的集合合并;第二种:2 x y 即把x所在的集合中删去x,并把x加入y所在的集合中;第三种:3 x 即输出x所在集合元素的个数和所有元素的和。

解题思路:此题关键点就是集合的删除操作,咱们可以换一个角度思考:首先删除某个元素x的结果是 这个集合中元素个数-1,所有元素的和-x,那直接对x元素所在的集合进行此操作,就让x在这个集合中的贡献为0了,也就相当于删除了x,但是现在又有一个问题:刚才的操作虽然相当于删除了x,但实际上x还在那个集合中,这个时候要把x合并到另外一个集合中,就需要找一个x的”替代品“,使得这个替代品与另外一个集合合并产生同样的效果,我们考虑开一个辅助数组id,id【x】为当前x的编号,如果x被删除,就产生一个新的编号代替当前x的编号。

详见代码:

#include<stdio.h>
#define N 200005
int f[N],rank[N],v[N],id[N];
int n,m,mark;
void init()
{
    for(int i=0;i<=n;i++)
    {
        f[i]=i;
        rank[i]=1;
        v[i]=i;
        id[i]=i;
    }
    mark=n;//mark必须在这里初始化,就这个纠结老长时间
}
int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}
void merge(int x,int y)
{
    int fx=find(id[x]);//查找最新状态x的父节点
    int fy=find(id[y]);
    if(fx!=fy)
    {
        f[fx]=fy;
        rank[fy]+=rank[fx];//更新秩
        v[fy]+=v[fx];//更新和
    }
}
void del(int x,int y)
{
    int fx=find(id[x]);
    int fy=find(id[y]);
    if(fx!=fy)//如果在一个集合中就不用进行操作
    {
        //消除对原集合的影响,并找一个“替代品”
        rank[fx]--;
        v[fx]-=x;
        id[x]=++mark;
        //初始化“替代品”,并进行合并操作,和下面注释掉的等效
        f[id[x]]=id[x];
        rank[id[x]]=1;
        v[id[x]]=x;
        merge(x,y);
        //因为“替代品”在下面的合并中不为根节点,所以“替代品”需要保留的信息
        //只是自身的位置信息,而这个信息在id[x]=++mark已经更新过,故直接计算
        //其对所要合并集合的影响即可
        /*f[id[x]]=fy;
        rank[fy]++;
        v[fy]+=x;*/
    }
}
int main()
{
    int k,x,y;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0;i<m;i++)
        {
            scanf("%d",&k);
            if(k==1)
            {
                scanf("%d%d",&x,&y);
                merge(x,y);
            }
            else if(k==2)
            {
                scanf("%d%d",&x,&y);
                del(x,y);
            }
            else
            {
                scanf("%d",&x);
                int ans=find(id[x]);
                printf("%d %d\n",rank[ans],v[ans]);
            }
        }
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值