题意很简单,有两个操作,一个是I p q v告诉你 x[p]^x[q]=v,一个是查询k个数的异或和,问是否出现矛盾以及能否确定答案
很经典的并查集问题
首先我们可以虚拟一个根节点x[n]=0,这样所有的I p v都可以转换成 x[p]^x[n]=0.另w[a]=x[a]^x[f[a]]即本身和父节点的异或值, 那么如何合并呢?记fp=f[p],fq=f[q],x[p]^x[q]=v,另f[fp]=fq的话,w[fp]=x[fp]^x[fq]=(x[p]^x[fp])^(x[p]^x[q])^(x[q]^x[fq])=w[p]^v^w[q],由此合并。难点在查询。x[a1]^x[a2]^...^x[ak]=w[a1]^w[a2]^..^w[ak]^x[f[a1]]^x [f[a2]]^...^x[f[ak]],前一部分直接求即可,后一部分只需求父节点出现次数为奇数次的,异或起来即可
github代码
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int N=20005;
int ca,ca2,p,q,v,k,x,n,Q,f[N],w[N];
char s1[20],s2[20];
bool know,bug;
int find(int x){
if(f[x]!=x){
int fx=find(f[x]);
w[x]^=w[f[x]];
f[x]=fx;
}
return f[x];
}
void Union(int x,int y,int v){
int fx=find(x);
int fy=find(y);
if(fx==fy){
if((w[x]^w[y])!=v) bug=true;
return;
}
if(fx==n) swap(fx,fy);
f[fx]=fy;
w[fx]=w[x]^v^w[y]; //>_<
}
int main(){
while(scanf("%d%d",&n,&Q)&&n+Q){
++ca;
ca2=0;
bug=false;
for(int i=0;i<=n;++i) f[i]=i,w[i]=0;
printf("Case %d:\n",ca);
while(Q--){
scanf("%s",s1);
if(s1[0]==‘I‘){
++ca2;
gets(s1);
if(bug) continue;
int cnt=sscanf(s1,"%d%d%d",&p,&q,&v);
if(cnt==2) v=q,q=n;
Union(p,q,v);
if(bug) {printf("The first %d facts are conflicting.\n",ca2);continue;}
}
else{
know=true;
scanf("%d",&k);
int ans=0;
map<int,int>mp;
while(k--){
scanf("%d",&x);
int fx=find(x);
ans^=w[x];
mp[fx]++;
}
if(bug) continue;
map<int,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++){
if(it->second%2){
if(it->first!=n) {know=false;break;}
else ans^=w[it->first];
}
}
if(!know) printf("I don‘t know.\n");
else printf("%d\n",ans);
}
}
printf("\n");
}
return 0;
}
Hdu 3234 & Uva 12232 Exclusive-OR
原文:http://blog.csdn.net/mlzmlz95/article/details/18815471