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:Input

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.

Sample Input

5 7

1 1 2

2 3 4

1 3 5

3 4

2 4 1

3 4

3 3

Sample Output

3 12

3 7

2 8

题意:给了三种操作:

           1. 合并 p , q的集合

           2. 把 p 加入到p的集合里

           3.求p集合内的元素个数,以及元素和,并输出

思路:对于1,3操作很简单就可以处理(带权值的并查集),关键在于操作2,我们不能无脑般的直接改变p的祖先,因为那样p的子孙们的祖先也改变了。考虑到不能直接改变p的祖先,那么我们就可以重新建立一个节点,这个节点只有自己,然后再进行并查集就好了。我们用 id [ x ]·表示 x 的最新节点是谁。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
#define ll long long
using namespace std;
int n,m,f[N],id[N];
ll Sum[N],ans[N];
void init()
{
    for(int i=0; i<N; i++)
    {
        f[i]=i;
        id[i]=i;
        Sum[i]=i;
        ans[i]=1;
    }
}
int getf(int v)
{
    if(f[v]==v)
        return v;
    int d=getf(f[v]);
    Sum[v]+=Sum[f[v]];
    ans[v]+=ans[f[v]];
    return f[v]=d;
}
void merge(int x,int y)
{
    int t1=getf(id[x]);
    int t2=getf(id[y]);
    f[t1]=t2;
    Sum[t2]+=Sum[t1];
    ans[t2]+=ans[t1];
}
void create(int x)   //新建立一个节点
{
    int t1=getf(id[x]);  //先找到祖先
    Sum[t1]-=x;         //删去这个点的值
    ans[t1]--;          
    id[x]=++n;         //新建立一个节点为 ++n 的节点,当做 id[x] 的新节点
    f[id[x]]=id[x];   //节点初始化
    Sum[id[x]]=x;
    ans[id[x]]=1;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(op==1)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                int t1=getf(id[x]);
                int t2=getf(id[y]);
                if(t1!=t2)
                    merge(x,y);
            }
            else if(op==2)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                int t1=getf(id[x]);
                int t2=getf(id[y]);
                if(t1!=t2)
                {
                    create(x);  //新建立一个节点
                    merge(x,y);  //合并
                }
            }
            else
            {
                int x;
                scanf("%d",&x);
                int t1=getf(id[x]);
                printf("%lld %lld\n",ans[t1],Sum[t1]);
            }
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值