http://acm.hdu.edu.cn/showproblem.php?pid=3234
1. 对于第一种命令I p v,我们可以虚拟出一个点Xn = 0,那么p^Xn = v,故第一和第二种命令我们可以统一成p^q = v的模式。
2. 对于第三种命令,记val[i] = Xi^Xfa[i],那么Xp1^Xp2^...^Xpk = (val[Xp1]^...^val[Xpk]) ^ (Xfa[Xp1]^...^Xfa[Xpk])
val[Xp1]^...^val[Xpk]易求,对于Xfa[Xp1]^...^Xfa[Xpk],由于a^c^b^c = a^b,可先判断Xfa[Xpi]出现的次数的奇偶性,偶数不用管,奇数看Xfa[Xpi]是否为Xn,若不是则在Xp1^Xp2^...^Xpk中必有未知信息,若是则异或Xfa[Xpi]即可。
完整代码:
/*0.102s*/
#include<bits/stdc++.h>
using namespace std;
const int mx = 20005;
int val[mx], fa[mx], n;
char str[100];
int find(int x)
{
if (~fa[x])
{
int tmp = fa[x];
fa[x] = find(fa[x]);
val[x] ^= val[tmp]; ///pushdown
return fa[x];
}
return x;
}
bool merge(int x, int y, int v)
{
int fx = find(x), fy = find(y);
if (fx == fy) return (val[x] ^ val[y]) == v;
if (fx == n) swap(fx , fy); ///技巧:始终使得Xn(虚拟的点)为根结点
fa[fx] = fy, val[fx] = val[x] ^ v ^ val[y];
return true;
}
int main()
{
//freopen("in.txt", "r", stdin);
int m, p, q, v, k, x, ans, cas = 0, facts;
bool isError;
map<int, int> mp;
map<int, int>::iterator iter;
while (scanf("%d%d", &n, &m), n)
{
getchar();
printf("Case %d:\n" , ++cas);
memset(val, 0, sizeof(val));
memset(fa, -1, sizeof(fa));
facts = 0;
isError = false;
while (m--)
{
if (getchar() == 'I')
{
gets(str);
++facts;
if (isError) continue;
if (sscanf(str, "%d%d%d", &p, &q, &v) == 2) v = q, q = n; ///换成根结点
if (!merge(p, q, v))
{
isError = true;
printf("The first %d facts are conflicting.\n" , facts++);
}
}
else
{
scanf("%d", &k);
ans = 0;
mp.clear();
while (k--)
{
scanf("%d", &x);
if (isError) continue;
find(x); ///更新一下值再计算
ans ^= val[x];
++mp[find(x)];
}
getchar();
if (isError) continue;
for (iter = mp.begin(); iter != mp.end(); ++iter)
{
if (iter->second & 1)
{
if (iter->first != n) break; ///存在未知信息
else ans ^= val[iter->first];
}
}
if (iter == mp.end()) printf("%d\n" , ans);
else puts("I don't know.");
}
}
putchar(10);
}
return 0;
}