uva 12232 - Exclusive-OR(加权并查集)

737 篇文章 0 订阅

题目链接:uva 12232 - Exclusive-OR

题目大意:有n个数,一开始并不知道具体的值,现在进行Q次操作。

  • I u k:au的值为k
  • I u v k:auav=k
  • Q k q1q2qk:求q1q2qk
    对于Q操作不能确定的话输出"I don't know."
    对于I操作矛盾的话则输出是第几条I操作出现矛盾的,并且停止后面所有的操作。

解题思路:加权并查集,f[x]表示x节点父亲节点,d[x]表示x节点与其父节点的亦或值,对于确定的节点值,可以将父亲节点设为0,这样的话在合并操作的时候就要注意,如果有一段0是父亲节点的话,就要将令一个节点指向0节点(也就是说0节点是固定不能有父亲节点的),一是在指向0节点的联通集合是确定值的,在查询是需要特判,二是进行第一种操作的时候方便处理。
在查询时,需要将所有的数分成各个联通集合,如果存在联通集合的个数为奇数,并且根节点不为0的话就是不能确定值的,否则取所有节点与父亲节点的亦或值的亦或和即可。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 20005;
const int maxm = 105;

bool flag;
int N, Q, f[maxn], d[maxn];

int getfar (int x) {

    if (x == f[x])
        return x;

    int tmp = f[x];
    f[x] = getfar(f[x]);
    d[x] ^= d[tmp];
    return f[x];
}

void query () {
    int n, ret = 0, num[maxm], vis[maxm];
    memset(vis, 0, sizeof(vis));

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &num[i]);
        num[i]++;
    }

    if (flag)
        return;

    for (int i = 0; i < n; i++) {

        int root = getfar(num[i]), cnt = 1;
        if (root == 0 || vis[i])
            continue;

        for (int j = i+1; j < n; j++) {
            if (getfar(num[j]) == root) {
                cnt++;
                vis[j] = 1;
            }
        }

        if (cnt&1) {
            printf("I don't know.\n");
            return ;
        }
    }

    for (int i = 0; i < n; i++)
        ret ^= d[num[i]];
    printf("%d\n", ret);
}

bool link () {
    char s[maxm];
    gets(s);

    int u, v, k;
    int type = sscanf(s, "%d%d%d", &u, &v, &k);
    u++;

    if (type == 2) {
        k = v;
        v = 0;
    } else
        v++;

    int p = getfar(u);
    int q = getfar(v);

    if (p == 0)
        swap(p, q);

    if (p != q) {
        f[p] = q;
        d[p] = d[u]^d[v]^k;
    } else
        return (d[u]^d[v]) != k;
    return false;
}

int main () {
    int cas = 1;
    while (~scanf("%d%d", &N, &Q) && N) {

        printf("Case %d:\n", cas++);

        flag = false;
        memset(d, 0, sizeof(d));
        for (int i = 0; i <= N; i++)
            f[i] = i;

        int ti = 0, u, v;
        char s[maxm];
        for (int i = 0; i < Q; i++) {
            scanf("%s", s);

            if (s[0] == 'I') {
                ti++;
                if (link()) {
                    flag = true;
                    printf("The first %d facts are conflicting.\n", ti);
                }
            } else if (s[0] == 'Q')
                query();
        }
        printf("\n");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值