題目
題目敘述來自思路來源,懶得敲了2333……
思路來源
https://blog.csdn.net/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;
}