题目
题目叙述来自思路来源,懒得敲了2333……
思路来源
https://blog.youkuaiyun.com/XY20130630/article/details/50638922
题解
考虑,把Xp XOR Xq=v这样的关系建成边,不妨p为root,则q与其相连,且置q的置为v,代表一种相对关系
后续,带权并查集维护的,也都是到祖先的这条链的异或值
Xp=v的一类型操作,将其视为Xp XOR 0=v,
由于原先的点的下标为0到n-1,则建一个虚点n,作一类型的0点,
考虑合并过程,如果相同祖先,但是到根的距离不满足差z的关系,则不合法
否则合法,按带权并查集的思想,若将px合并到py上去,
则将px的距离用py->y y->x x->px的关系,重新定义px的值
然后考虑询问,对每一堆相同rt的单独考虑,不同root的互不影响
不妨设x1,x2,...,xm的根相同,则由于每个值v[xi]是与根root的异或值,
最后的答案是,
如果root的个数为偶,显然是可以消掉的;
为奇的话,由于root的值可以改变式子的值,故此时答案不唯一
具体实现的时候,特判一下root是不是虚点n
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10,M=300,K=20;
int n,q,k,x,y,z,par[N],v[N],ask[K];
char s[N];
bool vis[K];
void init(int n){
for(int i=0;i<=n;++i){
par[i]=i;
v[i]=0;
}
}
int find(int x){
if(par[x]==x)return x;
int fa=par[x];//直接基类
par[x]=find(par[x]);//路径压缩
v[x]^=v[fa];//树上前缀和 两链变一链 直接连树根
return par[x];//返回树根
}
//y^x=z
bool unite(int x,int y,int z){
int px=find(x),py=find(y);
if(px==py){
if((v[x]^v[y])!=z)return 0;
return 1;
}
if(px==n){//n为不动点 只能为根
swap(px,py);
}
par[px]=py;
v[px]=v[y]^z^v[x];
return 1;
}
int query(){
int fa,ans=0,num;
for(int i=1;i<=k;++i){
if(vis[i])continue;
vis[i]=1;
fa=find(ask[i]);
ans^=v[ask[i]];
num=1;
for(int j=i+1;j<=k;++j){
if(vis[j])continue;
if(find(ask[j])==fa){
vis[j]=1;
ans^=v[ask[j]];
num++;
}
}
if((num&1) && fa!=n){
return -1;
}
}
return ans;
}
int main(){
int c=0;
while(~scanf("%d%d",&n,&q)){
if(!n && !q)break;
bool non=false;
int fac=0;
init(n);
printf("Case %d:\n",++c);
while(q--){
scanf("%s",s);
if(s[0]=='I'){
fac++;
getchar();
fgets(s,250,stdin);
int sp=0;//space
for(int i=0;s[i];++i){
sp+=(s[i]==' ');
}
if(sp==1){
sscanf(s,"%d%d",&x,&z);
y=n;
}
else{
sscanf(s,"%d%d%d",&x,&y,&z);
}
if(non)continue;
if(!unite(x,y,z)){
printf("The first %d facts are conflicting.\n",fac);
non=true;
}
}
else{
scanf("%d",&k);
for(int i=1;i<=k;++i){
vis[i]=0;
scanf("%d",&ask[i]);
}
if(non)continue;
int ans=query();
if(ans==-1)puts("I don't know.");
else printf("%d\n",ans);
}
}
puts("");
}
return 0;
}