hdu3234 Exclusive-OR(帶權並查集)

題目

題目敘述來自思路來源,懶得敲了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的異或值,

最後的答案是,v_{x1}\bigoplus v_{x2}\bigoplus ... \bigoplus v_{xm}=x_{1}\bigoplus root\bigoplus x_{2}\bigoplus root\bigoplus ...\bigoplus x_{m}\bigoplus 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章