标签:
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:
Format | Meaning |
I p v | I tell you Xp = v |
I p q v | I tell you Xp XOR Xq = v |
Q k p1 p2...pk | Please tell me the value of Xp1 XOR Xp2 XOR...XOR Xpk |
There will be at most 10 test cases. Each case begins with two integers n and Q (1n20, 000, 2Q40, 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.
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.
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 2 Case 2: 4 Case 3: 7The first 2 facts are conflicting.
题意:两种操作,I p q v表示p^q = v,如果与之前有冲突, 则输出“The first i facts are conflicting.”其中i为之前所有的I操作的次数(算上当前冲突这次)。Q k p1p2..pk表示求p1^p2...^pk的值,输出值或“I don‘t know.”
思路:本题可以用坑爹来形容,首先我们需要确定,一个连通块如何确定2点间的异或值。一个值被异或偶数次,就等于没异或。那么每个节点保存的是它与父节点的异或值,当你把每个节点的值异或起来时,如果它们中有父节点被异或了奇数次,那么这时你就多异或了一个不要异或的值。如果这个父节点是虚结点n的话,那没关系因为n的d值是0,异或它等于本身。但是如果不是n那么就判断不出。因为这个多异或的父节点值不知道它的d值是多少。
所以我们用d[i]表示i与其父节点的异或值,输入p q的时候就利用并查集来更新d。如果只输入p时怎么办呢?不妨建立一个虚节点n,如果输入的是一个节点的值,就让它的父节点指向n,这样d[p]就是p与n的异或了(n初始为0)。输入的时候判断是否有冲突,如果没有的话,就合并,n总是要做为父节点的(如果有的话)。
然后是询问部分。假如询问发生在一个集合中,包含k个点,如果k为偶数,那么直接把k个d[]异或起来就是答案;如果k是奇数,直接异或会多一个根的值。理由:经过find(i)之后,d[i]表示i与该集合根节点的异或值,那么偶数次异或同一个值就相当于什么都没有发生;奇数次呢就会多异或了一个根节点的值,而我们一定不知道根结点的值,除非根节点为n,因为所有已知值的节点都是n的子结点。
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=20000+10; char str[2],s[20]; int d[maxn],f[maxn],vis[maxn]; int n,m; int find(int x) { if(x!=f[x]) { int t=f[x]; f[x]=find(f[x]); d[x]^=d[t]; return f[x]; } else return x; } bool add(int p,int q,int v) { int x=find(p); int y=find(q); if(x==y)return v==(d[p]^d[q]); if(x==n)swap(x,y); f[x]=y; d[x]=d[p]^d[q]^v; return true; } int a[20]; int main() { #ifndef ONLINE_JUDGE freopen("in.cpp","r",stdin); freopen("out.cpp","w",stdout); #endif // ONLINE_JUDGE int cas=1; while(scanf("%d %d",&n,&m)!=EOF&&n+m) { int flag=0; int wa=0; int p,q,v; for(int i=0;i<=n;i++)f[i]=i; memset(vis,false,sizeof(vis)); memset(d,0,sizeof(d)); printf("Case %d:\n",cas++); while(m--) { scanf("%s",str); if(flag) { gets(s); continue; } if(str[0]=='I') { wa++; gets(s); if(sscanf(s,"%d%d%d",&p,&q,&v)==2) { v=q; q=n; } if(!add(p,q,v)) { printf("The first %d facts are conflicting.\n",wa); flag=1; } } else { int k; scanf("%d",&k); v=0; q=1; for(int i=0;i<k;i++) { scanf("%d",&a[i]); p=find(a[i]); v^=d[a[i]]; a[i]=p; vis[p]=!vis[p]; } for(int i=0;i<k;i++) { if(vis[a[i]]) { if(a[i]!=n) q=0; vis[a[i]]=0; } } if(q)printf("%d\n",v); else printf("I don't know.\n"); } } printf("\n"); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
UVA alive 4487 Exclusive-OR(加权并查集+异或运算的理解)
标签:
原文地址:http://blog.csdn.net/u012313382/article/details/47045893