LA4487 Exclusive-OR (加权并查集)

来源:互联网 发布:知乎禁止转载怎么复制 编辑:程序博客网 时间:2024/06/06 07:34

题意: 有N个数X[0] ~ X[N], 事先并不知道他们的值,有三种操作(^为异或):

I P V :告诉你X[P] = V
I P Q V:告诉你X[P] ^ X[Q] = V
Q K P1..PK:询问X[P1]^X[P2]^...X[PK]的值

如果信息矛盾 或者 不能知道查询的结果输出一些提示信息。


思路:对于第一种操作, X[P] = V 可以看做第二种操作的 X[P] ^ 0 = V, 新增一个节点n, 他的值为0。

对于查询, 我们知道, 如果有a ^ b = x1, b ^ c = x2, 可以得出 a ^ c = x1 ^ x2, 那么如果Xa ^ Xb = v为a, b连接一条边,权值为v,则a^b能求出结果的条件是a, b有相同的根节点,这里可以使用并查集, 注意在合并的时候需要进行路径压缩, 即每一节点有一个与根节点异或的值。那么在查询的时候, 先把不同集合里面的节点分开, 相同集合里面的节点放一起, 在同一个集合里面, 如果根节点是n, 那么一定可以得出所有节点的异或值, 如果不为n, 那么只要出现节点个数为奇数的集合就不能得出结果, 否则将每个节点的值异或起来即可。


#include<cstdio>#include<cstring>#include<cmath>#include<vector>#include<iostream>#include<algorithm>const int maxn = 2e4 + 10;using namespace std;int n, Q;char cmd[maxn];int pa[maxn], dis[maxn];int num, r[50], fac;int read() {    int len = strlen(cmd);    num = 0;    for(int i = 0; i < len; i++) {        if(cmd[i] < '0' || cmd[i] > '9') continue;        int j = i, sum = 0;        while(j < len && cmd[j] >= '0' && cmd[j] <= '9') {            sum = sum * 10 + cmd[j] - '0';            j++;        }        r[num++] = sum;        i = j;    }    char ch = cmd[0];    if(ch == 'Q') return 3;    fac++;    if(num == 2) return 1;    return 2;}int findset(int x) {    if(x == pa[x]) return x;    int px = findset(pa[x]);    dis[x] ^= dis[pa[x]];    return pa[x] = px;}bool can_unit(int x, int y, int val) {    int nx = findset(x), ny = findset(y);    if(nx == ny && (dis[x] ^ dis[y]) != val) return false;    if(nx == ny) return true;    if(nx == n + 1) {        pa[ny] = nx;        dis[ny] = dis[x] ^ dis[y] ^ val;    } else {        pa[nx] = ny;        dis[nx] = dis[x] ^ dis[y] ^ val;    }    return true;}vector<int> G[50];int fa[50];int main() {    int kase = 1;    while(scanf("%d %d", &n, &Q) && n) {        fac = 0;        for(int i = 0; i <= n + 2; i++) {            pa[i] = i; dis[i] = 0;        }        gets(cmd);        bool ans = true;        printf("Case %d:\n", kase++);        while(Q--) {            gets(cmd);            if(!ans) continue;            int op = read();            if(op == 1) {                int p = r[0], v = r[1];                if(!can_unit(p, n + 1, v)) { ans = false; printf("The first %d facts are conflicting.\n", fac); }            } else if(op == 2) {                int p = r[0], q = r[1], v = r[2];                if(!can_unit(p, q, v)) { ans = false; printf("The first %d facts are conflicting.\n", fac); }            } else {                                int x = 0;                for(int i = 0; i < 20; i++) G[i].clear();                for(int i = 1; i < num; i++) {                    int id = r[i], nx = findset(id);                    bool have = false;                    for(int j = 0; j < x; j++) {                        if(fa[j] != nx) continue;                        have = true;                        G[j].push_back(id);                        break;                    }                    if(have) continue;                    G[x].push_back(id); fa[x] = nx;                    x++;                }                bool solve = true;                int res = 0;                for(int i = 0; i < x; i++) {                    if(fa[i] != n + 1 && G[i].size() % 2 == 1) { solve = false; break; }                    for(int j = 0; j < G[i].size(); j++) {                        int k = G[i][j];                        res ^= dis[k];                    }                }                if(!solve) printf("I don't know.\n");                else printf("%d\n", res);            }        }        printf("\n");    }    return 0;}