hdu 3234 異或(加權並查集)
阿新 • • 發佈:2019-02-13
有n(n<=20000)個未知的整數X0,X1,X2Xn-1,有以下Q個(Q<=40000)操作:
I p v :告訴你Xp=v
I p q v :告訴你Xp Xor Xq=v
Q k p1 p2 … pk : 詢問 Xp1 Xor Xp2 .. Xor Xpk, k不大於15。
如果當前的I跟之前的有衝突的話,跳出
思路就是並查集的擴充套件,每個節點表示他與根結點的異或值 。。。。思路略
ps:忘打了個.導致wa了好長時間............跪了
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> using namespace std; #define LL long long const LL maxn = 20000 + 5; const LL INF = 1000000000; LL x[maxn]; LL pa[maxn], num[maxn], n;//num陣列記錄每個集合中的元素個數 void init() { //增加了一個xn,即零結點 for(LL i = 0; i <= n; i++) { pa[i] = i; x[i] = 0;} } LL find(LL id) { if(id != pa[id]) { LL tmp = pa[id]; LL root = find(pa[id]); x[id] ^= x[tmp]; return pa[id] = root; } else return id; } bool unio(LL p, LL q, LL v) { LL pap = find(p), paq = find(q); if(pap == paq) { return (x[p] ^ x[q]) == v; //注意運算子的順序,如果不加括號會錯,位運算一定要小心優先順序 } if(pap == n) swap(pap, paq); pa[pap] = paq; x[pap] = x[p] ^ x[q] ^ v; // 最重要的一步,將兩顆樹連線在一起,很巧妙; return true; } int main() { //freopen("input.txt", "r", stdin); LL Q, kase = 1; while(scanf("%lld%lld", &n, &Q) && n) { init(); printf("Case %lld:\n", kase++); char cmd[2], str[20]; LL facts = 0; bool flag = true; for(LL i = 1; i <= Q; i++) { if(!flag){ gets(str); continue;} scanf("%s", cmd); if(cmd[0] == 'I') { facts++; gets(str); LL p, q, v; if(sscanf(str, "%lld%lld%lld", &p, &q, &v) == 2) { v = q; q = n; } if(!unio(p, q, v)) { printf("The first %lld facts are conflicting.\n", facts); flag = false; continue; } } else { LL k, idp[20], tag = 1; LL ans = 0; scanf("%lld", &k); for(LL i = 0; i < k; i++) { scanf("%lld", &idp[i]); num[find(idp[i])] = 0; } for(LL i = 0; i < k; i++) { num[find(idp[i])]++; ans ^= x[idp[i]]; } for(LL i = 0; i < k; i++) { if(num[find(idp[i])] % 2 == 1 && find(idp[i]) != n) { tag = 0; break; } } if(!tag) printf("I don't know.\n"); else printf("%lld\n", ans); } } printf("\n"); } return 0; }