扩展的并查集,参考了以下两个,结合了一下,然后对Query的部分有一些改动,主要是用了map来判断出现次数的奇偶
http://www.cppblog.com/Yuan/archive/2010/09/02/125667.html?opt=admin
http://blog.csdn.net/acm_cxlove/article/details/8101710
X1...Xn-1,这是题中所说的未给出的n个数,维护一个带权的并查集,每个点有一个权w
保证有w[k] = Xk ^ Xfa[k]; Xfa[k]表示Xk的父亲。
对于
I p q v
则对p, q进行Union操作,并利用v维护各自的权w
对于
I p v
可以加入虚拟节点n,令Xn = 0; 这样任意以该节点为父亲的节点均有 w[k] = Xk;
这样就转化成I p n v了
而对于每次查询
Q k p1 p2 ... pk
结果ans = Xp1 ^ Xp2 ^ ... ^ Xpk = w[p1] ^ w[p2] ^ ... ^ w[pk] ^ (Xfa[p1] ^ Xfa[p2] ^ ... ^ Xfa[pk]).
这里利用了性质:
任取整数a,有
a ^ a = 0;
a ^ 0 = a;
又因为 w[p1] ... w[pk] 是已知的, 只需判断 Xfa[p1] ... Xfa[pk] 是否已知即可,即看这些节点是否以Xn为根。
注意的一点是Xfa[p1] ... Xfa[pk] 中会有重复的,只要出现了偶数次就不用计算,只用考虑出现奇数次的就行了。
另一点注意的是输入输出的形式以及输入行尾换行符的处理,我就在这个地方WA了n多次= =
View Code
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<cstdio> 5 #include<queue> 6 #include<algorithm> 7 #include<map> 8 using namespace std; 9 10 const int N = 20000 + 1; 11 const int K = 15; 12 const int LINELEN = 150; 13 14 int fa[N], w[N]; 15 int n,q; 16 int nfacts; 17 char line[LINELEN]; 18 19 void init() 20 { 21 for(int i = 0; i<= n; i++) 22 fa[i] = i; 23 memset(w, 0, sizeof(w)); 24 25 nfacts = 0; 26 } 27 28 int Find(int x) 29 { 30 if(x != fa[x]) 31 { 32 int t = fa[x]; 33 fa[x] = Find(fa[x]); 34 w[x] ^= w[t]; 35 } 36 return fa[x]; 37 } 38 39 bool Union(int p, int q, int v) 40 { 41 int rp = Find(p); 42 int rq = Find(q); 43 if(rp == rq) 44 { 45 return v == (w[p] ^ w[q]); 46 } 47 48 if(rp == n) swap(rp,rq); 49 50 fa[rp] = rq; 51 w[rp] = w[p] ^ w[q] ^ v; 52 return true; 53 } 54 55 bool info(int a, int b, int c) 56 { 57 bool rt = Union(a, b, c); 58 if(!rt) 59 printf("The first %d facts are conflicting.\n", nfacts); 60 return !rt; 61 } 62 63 int main(int argc, char *argv[]) 64 { 65 #ifdef ACM 66 freopen("ttwo.in", "r", stdin); 67 #endif // ACM 68 69 int ncase = 1; 70 while(scanf("%d %d", &n, &q), n != 0 || q != 0) 71 { 72 printf("Case %d:\n", ncase++); 73 init(); 74 bool conflict = false; 75 76 while(q--) 77 { 78 char tp[5]; 79 scanf("%s", tp); 80 if(tp[0] == 'I') 81 { 82 nfacts++; 83 getchar();gets(line); 84 int a, b, c; 85 int rt = sscanf(line, "%d %d %d", &a, &b, &c); 86 if(rt == 2) 87 { 88 c = b; b = n; 89 } 90 91 if(conflict) continue; 92 93 conflict = info(a, b, c); 94 } 95 else if(tp[0] == 'Q') 96 { 97 int k; 98 int para; 99 map<int, bool> fas; 100 bool known = true; 101 int ans = 0; 102 103 scanf("%d", &k); 104 for(int i = 0; i!= k; i++) 105 { 106 scanf("%d", ¶); 107 108 if(conflict) continue; 109 110 fas[Find(para)] = !fas[Find(para)]; 111 ans ^= w[para]; 112 } 113 114 if(conflict) continue; 115 116 for(map<int, bool>::iterator it = fas.begin(); it != fas.end(); it++) 117 { 118 if(it->second) 119 { 120 ans ^= w[it->first]; 121 if(!(known = (Find(it->first) == n))) 122 break; 123 } 124 } 125 126 if(!known) 127 { 128 puts("I don't know."); 129 } 130 else 131 { 132 printf("%d\n", ans); 133 } 134 } 135 } 136 137 putchar('\n'); 138 } 139 140 return 0; 141 }
BTW,做惯了USACO,发现对这种复杂的IO真心不习惯了= =,WA了何止10次啊.............= =
最后的问题竟然是.............Case的C忘了大写= =...........................................

浙公网安备 33010602011771号