给你n个未知的正整数,有三总操作
I P V P的值是V
I P Q V P XOR Q = V
Q K x1 x2....xk 求这k个数所有异或后的值
思路:
带权并查集,感觉这个题目用的很巧,设计到以下知识,a ^ b = c ,a ^ c = b ,b
^ c = a他们三个是等价的,还有就是a ^ b ^ a = b ,这个题目自己好好画画就出来了,确定好带权并查集后可以虚拟出来一个点,把它做为真实点,就是如果谁的根是他那么这个值就已经确定了,我虚拟的是0点,把其他的点都映射成+1,还有就是这个题目的核心部分就是查询的那个地方,除了确定的点,其他的必须是每个集合都出现了偶数个的时候才能算出来,原因就是“性质不同的数不能乘在一起,要么就是相对位置相乘,要么就是确定的数字相乘,两个相对位置相乘的到的是确定的数字,确定的数字相乘得到的还是确定的数字”,这个题目设计到很多细节,我就不说了,谁做谁知道啊! 还有就是提醒个最坑的地方 a ^ b != c 他和 (a ^ b) != c不是等价的,优先级的原因。其他的做的时候就知道了,今天手残,这个题目做了17次才AC.
#include<stdio.h> #include<string.h> #define N 22000 int mer[N] ,Xor[N]; int ss[N]; int finds(int x) { if(x == mer[x]) return x; int t = mer[x]; mer[x] = finds(mer[x]); Xor[x] = Xor[x] ^ Xor[t]; return mer[x]; } int main () { int n ,m ,i ,j ,num ,p ,q ,v ,k; int n1 ,n2 ,n3 ,cas = 1 ,fact ,stop; char str[10] ,c; while(~scanf("%d %d" ,&n ,&m) && n + m) { printf("Case %d: " ,cas ++); for(i = 0 ;i <= n ;i ++) mer[i] = i ,Xor[i] = 0; for(stop = fact = 0 ,i = 1 ;i <= m ;i ++) { scanf("%s" ,str); if(str[0] == 'I') { fact ++; int ii = 0; while(1) { scanf("%d%c" ,&num ,&c); ii ++; if(ii == 1) n1 = num; if(ii == 2) n2 = num; if(ii == 3) n3 = num; if(c == ' ') break; } if(stop) continue; if(ii == 2) { p = n1 + 1 ,v = n2; int x = finds(p); if(!x) { if(Xor[p] == v) continue; stop = 1; printf("The first %d facts are conflicting. " ,fact ++); } else { mer[x] = 0; Xor[x] = Xor[p] ^ v; } } if(ii == 3) { p = n1 + 1 ,q = n2 + 1 ,v = n3; int x = finds(p) ,y = finds(q); if(x == y) { if((Xor[p] ^ Xor[q]) == v) continue; stop = 1; printf("The first %d facts are conflicting. " ,fact ++); } else { if(y) { mer[y] = x; Xor[y] = Xor[p] ^ Xor[q] ^ v; } else { mer[x] = y; Xor[x] = Xor[p] ^ Xor[q] ^ v; } } } } else { scanf("%d" ,&k); memset(ss ,0 ,sizeof(ss)); int sum = 0 ,mk = 0; for(j = 1 ;j <= k ;j ++) { scanf("%d" ,&num); num ++; ss[finds(num)] ++; sum = sum ^ Xor[num]; } for(j = 1 ;j <= n ;j ++) if(ss[j] & 1) mk = 1; if(stop) continue; if(mk) puts("I don't know."); else printf("%d " ,sum); } } puts(""); } return 0; }