ZOJ_3789_Gears(可统计集合元素个数和可删除结点的带权并查集)

题型:数据结构


题意:

有n个齿轮,4种操作:

L  a b : 将a和b齿轮放在一起,两个齿轮放在一起,则属于一个集合,且转动方向相反。

Q a b : 查询a和b的转动方向是否相同,答案为Same、Different、Unknown

D a    : 将a齿轮从集合中拿出来

S a    : 查询a齿轮所在的集合中的元素个数


分析:

初始化根节点权值为0。

L a b时,将a和b加入一个集合,且b比a的权值大1。

Q a b时,若a和b的权值差为奇数,则方向相反,否则方向相同。 不在一个几个就无法查询。

D a时,由于被删除的节点的信息以后还会用到,所以就新建一个点作为该点拿出去。

S a时,直接查询a的祖先节点上储存的集合元素个数。


代码:

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#define M 612345
using namespace std;

int n,m;

int father[M],r[M],num[M];
int ts[M];

void init() {
    for(int i=1; i<=n+m; i++) {
        father[i] = -1;
        r[i] = 0;
        num[i] = 1;
        ts[i] = i;
    }
}

int Find(int x) {
    if(father[x] == -1) return x;
    int tmp = Find(father[x]);
    r[x] = (r[x] + r[father[x]]) % 2;
    father[x] = tmp;
    return father[x];
}

void Union(int x,int y) {
    int rootx=Find(x);
    int rooty=Find(y);
    if(rootx != rooty){
        r[rootx] = (r[y] - r[x] + 1) % 2;
        father[rootx] = rooty;
        num[rooty] += num[rootx];
    }
}

int main() {
    while(~scanf("%d%d",&n,&m)) {
        init();
        char str[10];
        int a,b;
        int now = n+1;
        while(m--) {
            scanf("%s",str);
            if(str[0] == 'L') {
                scanf("%d%d",&a,&b);
                Union(ts[a],ts[b]);
                continue;
            }
            if(str[0] == 'D') {
                scanf("%d",&a);
                num[Find(ts[a])]--;
                ts[a] = now++;
                continue;
            }
            if(str[0] == 'Q') {
                scanf("%d%d",&a,&b);
                if(Find(ts[a]) == Find(ts[b])) {
                    if(r[ts[a]] == r[ts[b]]) {
                        puts("Same");
                    } else {
                        puts("Different");
                    }
                } else {
                    puts("Unknown");
                }
                continue;
            }
            if(str[0] == 'S') {
                scanf("%d",&a);
                printf("%d\n",num[Find(ts[a])]);
                continue;
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值