UVA 11987 Almost Union-Find (并查集)

题意:这里有N个数编号1 ~N,开始每个数各自在一个集合里面,然后有三种命令:

1 P Q  :把 P 所在的集合和 Q 所在的集合合并,如果已经在一个集合里面了就忽视

2 P Q : 把 P 这个元素从它所在的集合里面拿出来放到 Q 里面,,如果已经在一个集合里面了就忽视

3 P :询问 P 所在集合元素的 个数 和P所在集合元素的 和。

思路:对于命令 1 直接合并就好了,用cnt[]数组代表元素个数,sum[]数组代表元素的和,合并的同时维护数组即可,对于命令 2 ,需要好好想一想,如果 P  不是根节点还好,如果是根节点的话,直接合并难免会出问题,所以可以重新开一个数组,d[],用来代表节点的编号,刚开始 d[i]存的都是i;当命令2 时 我们可以在题目所给的节点之外重新找个节点(1 ~N之外),比如 N +1.让 d[P] = N +1;(第二次就是N+2,总之就是重新找个不存在的节点来覆盖当前这个节点。)这样原来集合里面的那个 P  就失效了,我们合并 d[P] 和 d[Q]就好了,因为原来集合里面的 d[P] 节点已经不叫 P了,而是叫 N+1了,这样我们就不用考虑是否是根节点了。命令3直接查询就好了~~~具体看代码、、、、、

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
using namespace std;
const int MAXN =1e5 + 3;
int pre[MAXN*2],cnt[MAXN*2],sum[MAXN*2],d[MAXN*2];//依次为父节点数组,个数数组,求和数组,代号数组

int Find(int x)
{
    int r = x;
    while(pre[r] != r)
    {
        r = pre[r];
    }
    int i = x,j;
    while(pre[i] != r)
    {
        j = i;
        i = pre[i];
        pre[j] = r;
    }
    return r;
}
void Mix(int a,int b)
{
     int fa = Find(a),fb = Find(b);
     if(fa != fb)
     {
        pre[fa] = fb;
        sum[fb] += sum[fa];       //合并的同时维护两个数组
        cnt[fb] += cnt[fa];
     }
}
void make_set(int n)     //初始化初始化初始化
{
    for(int i = 1; i <= n; i++){
        pre[i] = i;
        sum[i] = i;
        cnt[i] = 1;
        d[i] = i;
    }
}

int main()
{
   //freopen("in.cpp","r",stdin);
    void debug(int n);
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        make_set(n);
        int temp = n + 1;
        while(m--)
        {
            int order;
            scanf("%d",&order);
            if(order == 1)       //命令一  直接合并就好了
            {
                int p,q;
                scanf("%d%d",&p,&q);
                Mix(d[p],d[q]);
              // debug(n);
            }
            else if(order == 2)              //命令二
            {
                int p,q;
                scanf("%d%d",&p,&q);
                int fp = Find(d[p]),fq = Find(d[q]);  
                 if(fp != fq) {
                    sum[fp] -= p;               //更新原来的集合数组
                    cnt[fp] --;
                    d[p] = temp++;         //新来了一个点代号 temp ,并覆盖原来的那个点,原来d[p] = p 现在等于 trmp 了
                    sum[d[p]] = p;       //新来这个点的 sum 数组初始化
                    cnt[d[p] ]= 1;     //cnt 数组初始化
                    pre[d[p]] = d[p];    //pre 数组初始化
                }
                Mix(d[p],d[q]);     //直接合并就好了
                   //  debug(n);
            }
            else if(order == 3)
            {
                int p;
                scanf("%d",&p);
               int fp = Find(d[p]);
                printf("%d %d\n",cnt[fp],sum[fp]);
            }
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值