题意: 有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;
}