AC通道:http://acm.hdu.edu.cn/showproblem.php?pid=3234
(注意,此网站的样例有点问题,每两个Case之间还需要增加一个空行)
异或
Description
有n个小于
220
的非负整数
X0,X1,…,Xn−1
,但你并不知道它们的值。
我会逐步提供一些信息,你的任务是根据这些信息回答问题。
如表:
指令 | 说明 |
---|---|
I
|
我告诉你 |
I
| 我告诉你 Xp XOR Xq = v |
你需要回答 Xp1 XOR Xp2 XOR … XOR Xpk 的值 |
Input
输入最多包含10组数据。
每组数据第一行为两个整数
n
和
以下
Q
行按顺序给出每条信息或者问题。
其中参数
信息和问题混在一起,在回答每个问题时只能依据那之前给出的信息。
输入结束标志为
n=Q=0
。
Output
对于每组数据,输出测试数据编号,然后是各个问题的答案。
如果某个问题的答案不能唯一确定,输出“I don’t know.”;
如果某条信息和之前的信息矛盾,输出“The first i facts are conflicting.”,然后忽略之后所有的问题。
每组数据之后输出一个换行。
Sample Input
2 6
I 0 1 3
Q 1 0
Q 2 1 0
I 0 2
Q 1 1
Q 1 0
3 3
I 0 1 6
I 0 2 2
Q 2 1 2
2 4
I 0 1 7
Q 2 0 1
I 0 1 8
Q 2 0 1
0 0
Sample Output
Case 1:
I don’t know.
3
1
2Case 2:
4Case 3:
7
The first 2 facts are conflicting.
Solution
本题可以利用加权并查集解决。
异或具有很多奇妙的性质:
-
a
^
- (
a
^
- 若
a
^
若
Xp
XOR
Xq
=
v
,那么我们可以把
若
所以我们就成功处理完了信息。
那么怎样回答呢?
对于有相同根节点的节点
a1
^
a2
^
a3
^ … ^
am
=
root
^
w1
^
root
^
w2
^ … ^
root
^
wm
综上所述,
- 若
root
的个数为奇数,及m为奇数,则这些节点的异或值为
root
^
w1
^
w2
^ … ^
wm
;
- 若
root
的个数为偶数,及m为偶数,则这些节点的异或值为
w1
^
w2
^ … ^
wm
。
若
m
为奇数且根结点的序号不为
否则就继续遍历下一个集合。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,q,TTT=0;
int vs[200100];
int wan[200];
char ques[100000];
bool wan2[200];
int fa[200100];
int find(int x){
int tmp;
if(fa[x]!=x)tmp=find(fa[x]);
else tmp=x;
vs[x]^=vs[fa[x]];
fa[x]=tmp;
return tmp;
}
bool merge(int x,int y,int z){
int fx=find(x),fy=find(y);
if(fx==fy){
if((vs[x]^vs[y])!=z)return false;
return true;
}
if(fx==n+1){
fa[fy]=fx;
vs[fy]=vs[x]^vs[y]^z;
return true;
}
fa[fx]=fy;
vs[fx]=vs[y]^vs[x]^z;
return true;
}
int read(int&x,int where){
x=0;
for(int i=where;;i++){
if(ques[i]>='0'&&ques[i]<='9')x=x*10+ques[i]-'0';
else return i-1;
}
}
int main(){
while(scanf("%d%d",&n,&q)!=EOF&&n+q){
TTT++;
printf("Case %d:\n",TTT);
bool flag=false;
getchar();
int TT=0;
for(int i=0;i<=n+1;i++)fa[i]=i,vs[i]=0;
for(int i=1;i<=q;i++){
fgets(ques,1000,stdin);
if(ques[0]=='I'){
TT++;
int t=2,x,y,z,l=strlen(ques);
int r=read(x,t);
int v=read(y,r+2);
if(flag)continue;
if(v==l-1||(v==l-2&&(ques[l-1]=='\n'||ques[l-1]=='\r'))){
if(!merge(x,n+1,y)){
printf("The first %d facts are conflicting.\n",TT);
flag=true;
}
}
else{
read(z,v+2);
if(!merge(x,y,z)){
printf("The first %d facts are conflicting.\n",TT);
flag=true;
}
}
}
else{
int t=2,k,ans=0;
int l=read(k,t);
for(int j=1;j<=k;j++)
l=read(wan[j],l+2);
if(flag)continue;
memset(wan2,0,sizeof wan2);
for(int j=1;j<=k;j++){
if(wan2[j])continue;
wan2[j]=true;
int fj=find(wan[j]),cnt=1;
ans^=vs[wan[j]];
for(int lss=j+1;lss<=k;lss++){
if(!wan2[lss]&&find(wan[lss])==fj){
wan2[lss]=true;
cnt++;
ans^=vs[wan[lss]];
}
}
if(fj!=n+1&&(cnt&1)){
printf("I don't know.\n");
goto nxt;
}
}
printf("%d\n",ans);
}
nxt:;
}
printf("\n");
}
return 0;
}