AC通道:http://acm.hdu.edu.cn/showproblem.php?pid=3234
(注意,此网站的样例有点问题,每两个Case之间还需要增加一个空行)
有n个小于 220 的非负整数 X0,X1,…,Xn−1 ,但你并不知道它们的值。
我会逐步提供一些信息,你的任务是根据这些信息回答问题。
如表:
指令 | 说明 |
---|---|
I p v | 我告诉你 Xp=v |
I p q v | 我告诉你 Xp XOR Xq = v |
Q k p1 p2 p3 … pk | 你需要回答 Xp1 XOR Xp2 XOR … XOR Xpk 的值 |
输入最多包含10组数据。
每组数据第一行为两个整数 n 和 Q ( 1≤n≤20000,2≤Q≤40000) 。
以下 Q 行按顺序给出每条信息或者问题。
其中参数 k 为不超过15的正整数, v 是小于 220 的非负整数。
信息和问题混在一起,在回答每个问题时只能依据那之前给出的信息。
输入结束标志为 n=Q=0 。
对于每组数据,输出测试数据编号,然后是各个问题的答案。
如果某个问题的答案不能唯一确定,输出“I don’t know.”;
如果某条信息和之前的信息矛盾,输出“The first i facts are conflicting.”,然后忽略之后所有的问题。
每组数据之后输出一个换行。
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
Case 1:
I don’t know.
3
1
2Case 2:
4Case 3:
7
The first 2 facts are conflicting.
本题可以利用加权并查集解决。
异或具有很多奇妙的性质:
- a ^ b = b ^ a ;
- ( a ^ b ) ^ c = a ^ ( b ^ c );
- 若 a ^ b = c ,那么 a ^ c = b 。
若 Xp XOR Xq = v ,那么我们可以把 Xp 与 Xq 连一条边,权值为 v 。
若 Xp = v ,那么就有 Xp XOR 0 = v ,所以我们可以把 Xp 与 n+1 连一条边,权值为 v 。
所以我们就成功处理完了信息。
那么怎样回答呢?
对于有相同根节点的节点 a1,a2,a3…,am ,设它们与根结点的距离为 wi ,则
a1 ^ a2 ^ a3 ^ … ^ am
= root ^ w1 ^ root ^ w2 ^ … ^ root ^ wm
综上所述,
- 若 root 的个数为奇数,及m为奇数,则这些节点的异或值为 root ^ w1 ^ w2 ^ … ^ wm ;
- 若 root 的个数为偶数,及m为偶数,则这些节点的异或值为 w1 ^ w2 ^ … ^ wm 。
若 m 为奇数且根结点的序号不为 n+1 ,那么就标志着答案不能唯一确定;
否则就继续遍历下一个集合。
#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;
}