两种操作,I p q v表示p^q = v,如果与之前有冲突, 则输出“The first i facts are conflicting.”其中i为之前所有的I操作的次数(算上当前冲突这次)。Q k p1p2..pk表示求p1^p2...^pk的值,输出值或“I don't know.”
首先,I操作后面跟的参数个数不确定所以用if(sscanf(s, "%d%d%d", &p, &q, &v) == 2)来判断参数的个数。
再有,用d[i]表示i与其父节点的异或值,输入p q的时候就利用并查集来更新d。如果只输入p时怎么办呢?不妨建立一个虚节点n,如果输入的是一个节点的值,就让它的父节点指向n,这样d[p]就是p与n的异或了(n初始为0)。输入的时候判断是否有冲突,如果没有的话,就合并,n总是要做为父节点的(如果有的话)。
然后是询问部分。假如询问发生在一个集合中,包含k个点,如果k为偶数,那么直接把k个d[]异或起来就是答案;如果k是奇数,直接异或会多一个根的值。理由:经过find(i)之后,d[i]表示i与该集合根节点的异或值,那么偶数次异或同一个值就相当于什么都没有发生;奇数次呢就会多异或了一个根节点的值,而我们一定不知道根结点的值,除非根节点为n,因为所有已知值的节点都是n的子结点啊。理解这个,就可以判断并求值了。
最后把所有d[pi]异或在一起就是答案,根节点每出现过一次就标记一下(我习惯用vis数组来标记),判断一下是否有出现过奇数次的,且是不是n。记得把数组清零,我那样写是因为k不超过15,循环也不费时间,但把整个vis数组都循环赋值一遍的话,还是要消耗一点时间的。
关键理解:一个值被异或偶数次,就等于没异或。那么每个节点保存的是它与父节点的异或值,当你把每个节点的值异或起来时,如果它们中有父节点被异或了奇数次,那么这时你就多异或了一个不要异或的值。如果这个父节点是虚结点n的话,那没关系因为n的d值是0,异或它等于本身。但是如果不是n那么就判断不出。因为这个多异或的父节点值不知道它的d值是多少。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<string> #include<queue> #include<cmath> ///LOOP #define REP(i, n) for(int i = 0; i < n; i++) #define FF(i, a, b) for(int i = a; i < b; i++) #define FFF(i, a, b) for(int i = a; i <= b; i++) #define FD(i, a, b) for(int i = a - 1; i >= b; i--) #define FDD(i, a, b) for(int i = a; i >= b; i--) ///INPUT #define RI(n) scanf("%d", &n) #define RII(n, m) scanf("%d%d", &n, &m) #define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k) #define RIV(n, m, k, p) scanf("%d%d%d%d", &n, &m, &k, &p) #define RV(n, m, k, p, q) scanf("%d%d%d%d%d", &n, &m, &k, &p, &q) #define RFI(n) scanf("%lf", &n) #define RFII(n, m) scanf("%lf%lf", &n, &m) #define RFIII(n, m, k) scanf("%lf%lf%lf", &n, &m, &k) #define RFIV(n, m, k, p) scanf("%lf%lf%lf%lf", &n, &m, &k, &p) #define RS(s) scanf("%s", s) ///OUTPUT #define PN printf("\n") #define PI(n) printf("%d\n", n) #define PIS(n) printf("%d ", n) #define PS(s) printf("%s\n", s) #define PSS(s) printf("%s ", n) ///OTHER #define PB(x) push_back(x) #define CLR(a, b) memset(a, b, sizeof(a)) #define CPY(a, b) memcpy(a, b, sizeof(b)) #define display(A, n, m) {REP(i, n){REP(j, m)PIS(A[i][j]);PN;}} using namespace std; typedef long long LL; typedef pair<int, int> P; const int MOD = 100000000; const int INFI = 1e9 * 2; const LL LINFI = 1e17; const double eps = 1e-6; const double pi = acos(-1.0); const int N = 22222; const int M = 22; const int move[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1}; int n; bool vis[N]; char op[2], s[M]; int f[N], d[N], a[M]; int find(int x) { if(x == f[x])return x; int tmp = f[x]; f[x] = find(f[x]); d[x] ^= d[tmp]; return f[x]; } bool add(int p, int q, int v) { int x = find(p); int y = find(q); if(x == y)return v == (d[p] ^ d[q]); if(x == n)swap(x, y); f[x] = y; d[x] = d[p] ^ d[q] ^ v; return 1; } int main() { //freopen("input.txt", "r", stdin); //freopen("output.txt", "w", stdout); int m, p, q, v, k, fact, go, cas = 1; while(RII(n, m), n + m) { REP(i, n + 1)f[i] = i; CLR(vis, 0); CLR(d, 0); fact = 0; go = 1; printf("Case %d:\n", cas++); while(m--) { RS(op); if(!go) { gets(s); continue; } if(op[0] == 'I') { fact++; gets(s); if(sscanf(s, "%d%d%d", &p, &q, &v) == 2)v = q, q = n; if(!add(p, q, v)) { printf("The first %d facts are conflicting.\n", fact); go = 0; } } else { RI(k); v = 0; REP(i, k) { RI(a[i]); p = find(a[i]); v ^= d[a[i]]; a[i] = p; vis[p] = !vis[p]; } q = 1; REP(i, k)if(vis[a[i]]) { if(a[i] != n)q = 0; vis[a[i]] = 0; } if(q)PI(v); else puts("I don't know."); } } puts(""); } return 0; }