带权并查集,思路很清晰,代码很难看。
对于 I u v ,分以下几种情况不合法。
1.num[u] 已确定 && num[u] != v 。
2.num[u] 的根已确定,若num[u] == v与根节点有冲突。
若合法,则修改num[u] 且 修改num[u]的根节点的信息。
对于 I u v w ,分以下几种情况不合法。
1.num[u] ,num[v] 均已确定或均可通过根节点确定 且 num[u]^num[v] != w;
2.若u,v在一个集合内,且num[u] ^ num[root] ^ num[v] ^num[root] = num[u]^num[v] != w;
若合法,且u,v不在一个集合内,合并u,v所在的集合,若num[root_u]已知,num[root_v]未知(设root_v合并后新集合的根),则更新num[root_v] 。
对于询问:
首先将确定的数的抑或结果计算出来,然后对于剩下的必须符合可以分成X份,每份中必须有偶数个元素。
因为任意两个同根的元素可以由num[u] ^ num[root] ^ num[v] ^num[root] = num[u]^num[v] 得出。
否则输出I don't know.
有些题解的解法简直炫酷到没朋友,通过虚拟出一个值为零的节点节省了很多代码,可见预先设计出好的思路是有多重要。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <stack>
#include <string>
#include <map>
#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long
#define _LL __int64
#define INF 0x3f3f3f3f
#define Mod 300
using namespace std;
struct N
{
int fa,re;
}st[20010];
int num[20010];
int Find(int x,int &R)
{
int f = x,re = 0,tre = 0,temp,tf;
while(f != st[f].fa)
{
re ^= st[f].re;
f = st[f].fa;
}
R = re;
while(x != st[x].fa)
{
temp = st[x].re;
tf = st[x].fa;
st[x].re = re^tre;
st[x].fa = f;
tre ^= temp;
x = tf;
}
return x;
}
bool Merge(int u,int v,int w)
{
int fu,fv,ru,rv;
fu = Find(u,ru);
fv = Find(v,rv);
if(num[fu] != -1)
num[u] = num[fu]^ru;
if(num[fv] != -1)
num[v] = num[fv]^rv;
if(num[u] != -1 && num[v] != -1)
return (num[u]^num[v]) == w;
if(fu == fv)
{
return (w^ru) == rv;
}
else
{
st[fu].fa = fv;
st[fu].re = w^rv^ru;
if(num[fv] == -1 && num[fu] != -1)
{
num[fv] = num[fu]^st[fu].re;
}
}
return true;
}
bool mark[20];
int qu[20];
void JudgeQ(char *s)
{
int i,j,k = 0;
for(i = 0;s[i] < '0' || s[i] > '9'; ++i)
;
for(;!(s[i] < '0' || s[i] > '9'); ++i)
;
for(;s[i] == ' '; ++i)
;
int anw = 0,temp,x;
while(s[i] != '\0')
{
temp = 0;
for(;!(s[i] < '0' || s[i] > '9'); ++i)
{
temp += (s[i]-'0');
temp *= 10;
}
temp = temp/10 + 1;
for(;s[i] == ' '; ++i)
;
qu[k++] = temp;
}
for(i = 0;i < k; ++i)
{
if(num[qu[i]] != -1)
{
anw ^= num[qu[i]];
continue;
}
if(num[Find(qu[i],x)] != -1)
{
num[qu[i]] = num[Find(qu[i],x)]^x;
anw ^= num[qu[i]];
}
}
memset(mark,false,sizeof(mark));
int ru,rv;
for(i = 0;i < k; ++i)
{
if(mark[i] == false && num[qu[i]] == -1)
{
for(j = i+1;j < k; ++j)
{
if(mark[j] == false && num[qu[i]] == -1 && Find(qu[i],ru) == Find(qu[j],rv))
{
mark[i] = true,mark[j] = true,anw ^= (ru^rv);
break;
}
}
if(mark[i] == false)
{
printf("I don't know.\n");
return;
}
}
}
printf("%d\n",anw);
}
int Judge(char *s)
{
int i;
for(i = 0;s[i] < '0' || s[i] > '9'; ++i)
;
for(;s[i] == ' '; ++i)
;
int ans = 0;
while(s[i] != '\0')
{
ans++;
for(;!(s[i] < '0' || s[i] > '9'); ++i)
;
for(;s[i] == ' '; ++i)
;
}
return ans;
}
int main()
{
int i,n,m,icase = 1;
char s[100],ord[100];
int u,v,w;
int ans;
bool wrong,first;
while(scanf("%d %d%*c",&n,&m) && (n||m))
{
for(i = 1;i <= n; ++i)
st[i].fa = i,st[i].re = 0,num[i] = -1;
wrong = false,first = true;
ans = 0;
printf("Case %d:\n",icase++);
while(m--)
{
gets(s);
if(first == false)
continue;
if(s[0] == 'I')
{
ans++;
if(Judge(s) == 3)
{
sscanf(s,"%s %d %d %d",ord,&u,&v,&w);
u++,v++;
if(Merge(u,v,w) == false)
wrong = true;
}
else
{
sscanf(s,"%s %d %d",ord,&u,&w);
u++;
if(num[u] == -1 && num[Find(u,v)] == -1)
{
num[u] = w;
num[Find(u,v)] = w^st[u].re;
}
else if(num[u] == -1)
{
if((num[Find(u,v)]^st[u].re) != w)
wrong = true;
else
num[u] = w;
}
else if(num[u] != w)
wrong = true;
}
if(wrong && first)
{
printf("The first %d facts are conflicting.\n",ans);
first = false;
}
}
else
{
JudgeQ(s);
}
}
printf("\n");
}
return 0;
}