带权并查集--uva12232 Exclusive-OR

X0 ~ Xn-1 共n个数,根据给定的信息,给出询问结果。

已知 I a b c 表示Xa ^ Xb = c,I a c表示Xa = c

询问Q K X1...Xk 求X1 ^ ...^ Xk 的结果。

1.令Xn = 0,将Xa = c转变成Xa ^ Xn = c,所以所有已知的数都和Xn在同一棵树(以n为根)

2.在询问时,X1 ^ ... Xk = Xf1 ^ ... Xfk ^ v[1] ^... ^ v[k]。其中Xfi未知,如果根出现的次数为偶数,则可消去,如果为奇数,如果Xfi的根(即Xi)的根为Xn,则数已知,可求,否则,就无法求出。

3.v[i]记录Xi与根Xfi的关系。

4.memset(v,0,sizeof(int) * n)就好了,不一定要到maxn的,特别是要经常初始化的。

只在乎奇偶的话^= 1也不错。

#include <cstdio>

#include <iostream>

#include <cstring>

using namespace std;


const int maxn = 2e5 + 50;


int f[maxn],v[maxn],cnt[maxn];

int n,q;

bool cft = false;


int find(int x)

{

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

    int tmp = f[x];

    f[x] = find(f[x]);

    v[x] ^= v[tmp];

    return f[x];

}

void init(int n)

{

    memset(v, 0, sizeof(int) * (n + 1));

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

        f[i] = i;

    }

    cft = false;

}

void merge(int a,int b,int c)

{

    int fa = find(a);

    int fb = find(b);

    if(fa == fb) {cft = ((v[a] ^ v[b]) != c); return;}

    if(fa > fb) swap(fa,fb);

    f[fa] = fb;

    v[fa] = v[a] ^ v[b] ^ c;

}

void query()

{

    memset(cnt, 0, sizeof(int) * n);

    int k,t,ans = 0,rt;

    scanf("%d",&k);

    for (int j = 0; j < k; j ++) {

        scanf("%d",&t);

        rt = find(t);

        cnt[rt] ^= 1;

        ans ^= v[t];

    }

    if(!cft){

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

            if(cnt[i]) {printf("I don't know.\n");return;}

        }

        printf("%d\n",ans);

    }

}

int main()

{

    int cn = 0;

    while (scanf("%d%d",&n,&q) != EOF) {

        getchar();

        if(n == 0 && q == 0) break;

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

        init(n);

        int th = 0;

        char op;

        int a = 0,b = 0,c = 0;

        for (int i = 1; i <= q; i ++) {

            scanf("%c",&op);

            if(op == 'I')

            {   th ++;

                scanf("%d%d%c",&a,&b,&op);

                if(op == ' '){scanf("%d",&c);getchar();}

                else if(op == '\n') {c = b,b = n;}

                if(!cft){

                    merge(a,b,c);

                    if(cft) printf("The first %d facts are conflicting.\n",th);

                }

            }

            else {

                query(); getchar();

            }

        }

        printf("\n");

    }

    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值