POJ 1703 - Find them, Catch them

Advanced Data Structures :: Disjoint Set


Description

某个市有两个帮派,且这个市中的所有人,有且仅有在一个帮派中。

告诉你一些信息,即哪两个人不在一个帮派中。

且多次询问两个人,是否在同一个帮派中,或者关系暂时不确定。

对于每个询问,输出询问结果。


Type

Advanced Data Structures :: Disjoint Set


Analysis

利用并查集来解题。

但是并查集通常用来表示,有着相同地位或身份的人,

因为这样的关系有传递性,如你是我的战友,他是你的战友,则我和他也是战友。

而敌对关系没有传递性(你是我的敌人,他是你的敌人,而他不一定是我的敌人),

因此无法用一般的并查集表示。


但是,这样的敌对关系,我们也可以用并查集来表示。

就是将并查集中的边,加权(也可以说是,对每个点增加一个偏移量)。

权值(或者偏移量)用来表示,该节点与父节点的距离

(例如,敌对关系时,距离为1,战友关系则为0),

在同一个并查集森林里,两点之间的距离之和对状态数(上述例子为2)取模,则可以得到两点间的关系。


加权之后,就可以解决这道问题了,并且路径压缩的优化依然可以在加权并查集中使用。


注意当市里只有两个人(不要吐槽)时,这两个人必然是不同团伙的。


Solution

// POJ 1703
// Find them, Catch them
// by A Code Rabbit

#include <cstdio>
#include <cstring>

const int MAXN = 100002;
const int MOD = 2;

struct DisjointSet {
    int p[MAXN];
    int w[MAXN];
    void Init(int);
    void Make(int x) { p[x] = x; }
    int Find(int x) {
        if (p[x] == x) return x;
        int res = Find(p[x]);
        w[x] = (w[x] + w[p[x]]) % MOD;
        return p[x] = res;
    }
    void Union(int x, int y, int d) {
        int px = Find(x); int py = Find(y);
        p[px] = py;
        w[px] = (w[y] - w[x] + d + MOD) % MOD;
    }
};

void DisjointSet::Init(int n) {
    memset(w, 0, sizeof(w));
    for (int i = 1; i <= n; i++)
        Make(i);
}

int n, m;
DisjointSet set;

int main() {
    int tot_case;   
    scanf("%d", &tot_case);
    while (tot_case--) {
        scanf("%d%d", &n, &m);
        getchar();
        set.Init(n);
        for (int i = 0; i < m; i++) {
            char ch; int a, b;
            ch = getchar();
            scanf("%d%d", &a, &b);
            getchar();
            if (ch == 'D') {
                set.Union(a, b, 1);
            } else {
                int pa = set.Find(a);
                int pb = set.Find(b);
                if (n == 2) {
                    puts("In different gangs.");
                    continue;
                }
                if (pa != pb)
                    puts("Not sure yet.");
                else
                    puts(set.w[a] == set.w[b] ?
                        "In the same gang." : "In different gangs.");
            }
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值