uva12232 - Exclusive-OR 加权并查集

You are not given n non-negative integers X0, X1,..., Xn-1 less than 220, but they do exist, and their values never change.

I'll gradually provide you some facts about them, and ask you some questions.

There are two kinds of facts, plus one kind of question:


FormatMeaning
I p vI tell you Xp = v
I p q vI tell you Xp XOR Xq = v
Q k p1 p2...pkPlease tell me the value of Xp1 XOR Xp2 XOR...XOR Xpk

Input 

There will be at most 10 test cases. Each case begins with two integers n and Q (1$ \le$n$ \le$20, 000, 2$ \le$Q$ \le$40, 000). Each of the following lines contains either a fact or a question, formatted as stated above. The k parameter in the questions will be a positive integer not greater than 15, and the v parameter in the facts will be a non-negative integer less than 220. The last case is followed by n = Q = 0, which should not be processed.

Output 

For each test case, print the case number on its own line, then the answers, one on each one. If you can't deduce the answer for a particular question, from the facts I provide you before that question, print ``I don't know.", without quotes. If the i-th fact (don't count questions) cannot be consistent with all the facts before that, print ``The first i facts are conflicting.", then keep silence for everything after that (including facts and questions). Print a blank line after the output of each test case.

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 
2 

Case 2: 
4 

Case 3: 
7 
The first 2 facts are conflicting.

  告诉你Xp=v或者Xp^Xq=v,询问一些Xi的异或值。

  如果没有Xp=v,只告诉你Xp^Xq=v就容易一些。只需要把Xp和Xq合并,设w[i]为Xi和它父节点的异或值,合并的时候注意w[i]=w[i]^w[p[i]]。查询的时候Xp1^Xp2...^Xpk=w[Xp1]^rootp1^w[Xp2]^rootp2...^w[Xpk]^rootpk,所以只要把w[Xpi]都异或上,由于root的具体值都是不知道的,于是若所有root都是偶数个,抵消了,答案就是w[Xpi]的异或。否则是不能算出答案的。

  告诉你Xp=v怎么办呢?这是个难点,要设置一个节点N,假设Xpn=0。把p和N合并,而且要保证N这个点一直是作为根节点。也就是说最后和N一个集合的X的值都是知道的,并且N是根节点。判断root奇偶的时候,若是N作为节点这个root就无所谓,因为这个值是知道的,就是0。也就是说除了N以外作为根的节点若出现奇数次,就无法确定答案。

  注意用异或符号的时候最好打括号。

#include<cstring>
#include<cstdio>
#include<iostream>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#define INF 0x3f3f3f3f
#define MAXN 20010
#define MAXM 3010
using namespace std;
int N,Q,bug,p[MAXN],w[MAXN];
char str[20];
int find(int x){
    if(p[x]==x) return x;
    int root=find(p[x]);
    w[x]^=w[p[x]];
    return p[x]=root;
}
void Union(int a,int b,int v){
    int x=find(a),y=find(b);
    if(x==y){
        if((w[a]^w[b])!=v) bug=1;   //这里如果不打括号就是错的
        return;
    }
    if(x==N) swap(x,y);      //N要始终作为根节点
    p[x]=y;
    w[x]=w[a]^w[b]^v;
}
int main(){
    freopen("in.txt","r",stdin);
    int cas=0;
    while(scanf("%d%d",&N,&Q),N||Q){
        printf("Case %d:\n",++cas);
        for(int i=0;i<=N;i++) p[i]=i;
        memset(w,0,sizeof(w));
        int k=0;
        bug=0;
        while(Q--){
            int a,b,v;
            scanf("%s",str);
            if(str[0]=='I'){
                k++;
                gets(str);
                if(bug) continue;
                if(sscanf(str,"%d%d%d",&a,&b,&v)==2){
                    v=b;
                    b=N;
                }
                Union(a,b,v);
                if(bug) printf("The first %d facts are conflicting.\n",k);
            }
            else{
                int K,t,ans=0,know=1;
                map<int,int> Map;
                map<int,int>::iterator it;
                scanf("%d",&K);
                while(K--){
                    scanf("%d",&t);
                    int x=find(t);
                    ans^=w[t];
                    Map[x]++;
                }
                if(bug) continue;
                for(it=Map.begin();it!=Map.end();it++) if(it->second%2){
                    if(it->first!=N){
                        know=0;
                        break;
                    }
                }
                if(know) printf("%d\n",ans);
                else printf("I don't know.\n");
            }
        }
        puts("");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值