uva 11987 Almost Union-Find(加权并查集)

Problem A

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:

1 p q

Union the sets containing p and q. If p and q are already in the same set, ignore this command.

2 p q

Move p to the set containing q. If p and q are already in the same set, ignore this command

3 p

Return the number of elements and the sum of elements in the set containing p.

Initially, the collection contains n sets: {1}, {2}, {3}, ..., {n}.

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). The size of input file does not exceed 5MB.

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

Output for the Sample Input

3 12
3 7
2 8

Explanation

Initially: {1}, {2}, {3}, {4}, {5}

Collection after operation 1 1 2: {1,2}, {3}, {4}, {5}

Collection after operation 2 3 4: {1,2}, {3,4}, {5} (we omit the empty set that is produced when taking out 3 from {3})

Collection after operation 1 3 5: {1,2}, {3,4,5}

Collection after operation 2 4 1: {1,2,4}, {3,5}


Rujia Liu's Present 3: A Data Structure Contest Celebrating the 100th Anniversary of Tsinghua University
Special Thanks: Yiming Li
Note: Please make sure to test your program with the gift I/O files before submitting!

这题是受luyuncheng的启发做出来的。

本题的难点就在2操作:
要把一个元素从一个集合移到另一个集合。
我们知道并查集是不能删除元素的,因为整个结构是单向的,不知道儿子是什么
但这里移动元素,实际上就蕴含着删除的操作。那该怎么办呢?
有一个想法可能会很快想到,并不实际删除要移动的元素p,即不改变p集合原来的路径,但集合的权值还是要改变(相当于把p看成一个虚点),然后把p直接“挂到”q的集合中去。
注意,这里一定要保证p在以后永远只能作为一个叶子,不能再做父亲,否则会覆盖掉最初(作为父亲时)的路径,并且可以证明只做叶子的做法一定存在。
这样在写程序时要注意的就有以下几点:
1.让每个元素的值有二维状态,一维用于保存,这个元素在最初集合时的情况,另一维保存这个元素现在实际的情况
2.在合并时注意权值的改变,应该用哪一维状态。
3.在find()时先判断这个元素是否真的存在过去和现在两个状态,从而选择搜索的路径不同,并且在写路径压缩时也有些不同。

详见代码:

#include <cstdio>

struct Node{
    int fa[2],num[2],value[2];
}ele[100005];

void init(){
    for(int i = 0;i < 100005;i++){
        ele[i].fa[0] = i;
        ele[i].fa[1] = -1;
        ele[i].num[0] = 1;
        ele[i].num[1] = 0;
        ele[i].value[0] = i;
        ele[i].value[1] = 0;
    }
}

int find(int n){
    if(ele[n].fa[1] == -1){
        int r = ele[n].fa[0];
        while(ele[r].fa[0] != r)
            r = ele[r].fa[0];
        int f = ele[n].fa[0];
        while(f != r){
            int tem = ele[f].fa[0];
            ele[f].fa[0] = r;
            f = tem;
        }
        return r;
    }
    else{
        int f = ele[n].fa[1];
        while(ele[f].fa[0] != f){
            f = ele[f].fa[0];
        }
        ele[n].fa[1] = f;
        return f;
    }
}

int n,m;
int main(){
    while(scanf("%d%d",&n,&m) != EOF){
        init();
        while(m--){
            int ope,p,q;
            scanf("%d",&ope);
            if(ope == 1){
                scanf("%d%d",&p,&q);
                int f1 = find(p);
                int f2 = find(q);
                if(f1 != f2){
                    ele[f1].fa[0] = f2;
                    ele[f2].num[0] += ele[f1].num[0];
                    ele[f2].value[0] += ele[f1].value[0];
                }
            }
            else if(ope == 2){
                scanf("%d%d",&p,&q);
                int f1 = find(p);
                int f2 = find(q);//忘了加if(f1 != f2),慢了一些
                ele[f1].num[0]--;
                ele[f1].value[0] -= p;
                ele[p].fa[1] = f2;
                ele[f2].num[0]++;
                ele[f2].value[0] += p;
            }
            else{
                scanf("%d",&p);
                int f = find(p);
                printf("%d %d\n",ele[f].num[0],ele[f].value[0]);
            }
        }
    }
    return 0;
}


 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值