NKOJ 3762 守夜人
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限1000ms
问题描述
鉴于john snow当选了新的守夜人总司令,艾里沙爵士感觉非常不爽,想搞点事情来难倒snow。艾里沙爵士告诉你有一个n项的序列X0,X1,X2…..Xn-1。(其中每一项均在int范围之内)但是你现在不知道其中的任何一项。艾里沙会逐步的告诉你一些信息并且问你一些问题。共有两种类型的信息和一种类型的询问。
I p v : tell you Xp = v
I p q v : tell you Xp XOR Xq = v
Q k p1 p2 … pk : 询问Xp1 XOR Xp2 XOR … XOR Xpk的值 k≤15
输入格式
会有多组测试数据但不会超过10组。
每一组数据以两个整数开始:n , Q(1 ≤ n ≤ 20, 000, 2 ≤ Q ≤ 40, 000)题目描述中的k是一个不大于15的整数。
最后一组数据为n==Q==0,不用进行运算。
输出格式
对于每组数据,输出第一行为数据的组数,接下来的每一行对应一次询问的答案。
如果根据前面给出的信息无法算出答案,则输出“I don’t know.”。
如果与已知信息冲突,输出“The first i facts are conflicting.”,并结束对于这一组数据的运算,其中i为这组数据出现过的的信息条数(不含询问,包含当前这一条信息)。
样例输入
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:
7
The first 2 facts are conflicting.
提示
注释:
鉴于两种I操作的输入比较麻烦,这里给出一种参考输入方法:
gets(s);
if(sscanf(s,”%d%d%d”,&a,&b,&v)==2)
//这一行输入了两个整数,要先除去行首的字母
{
…
}
来源 老王
思路:
并查集,将有关联的点加入一个集合,val[x]表示x^be[x].
特别的,建立一个虚拟点n,作为其所在集合的根,val[n]=0,即该集合中val[x]的值为x号本身。判断一个点是否已知,就看改点的集合根节点是否为n。
注意合并时,n优先作为父亲
对于Q,暴力枚举所有数,若已知,则^起来;若未知但be[x]出现次数为偶数,也可直接^起来,否则无解。
#include<cstdio>
#include<iostream>
#include<sstream>
using namespace std;
const int need=20004;
int val[need],be[need],cn[need];
//...........................................................
int getbe(int x)
{
if(x!=be[x])
{
int t=getbe(be[x]);
val[x]^=val[be[x]];
return be[x]=t;
}
return x;
}
void addbe(int x,int fx,int y,int fy,int z)
{
be[fx]=fy;
val[fx]=val[x]^val[y]^z;
}
//...........................................................
string c;
int cnt,k[17],a;
void in_()
{
cnt=0;
getline(cin,c,'\n');
stringstream os(c);
while(os>>a) k[++cnt]=a;
}
//...........................................................
int main()
{
//freopen("a.txt","r",stdin);
int n,m,f1,f2,tot=0,ans2;
char t;
bool mark;
while(true)
{
scanf("%d%d",&n,&m);
if(n==0) return 0;
printf("Case %d:\n",++tot);
mark=true;
ans2=0;
for(int i=0;i<=n;i++) be[i]=i,val[i]=0;
for(int i=1,j;i<=m;i++)
{
while(true)
{
t=getchar();
if(t=='I'||t=='Q') break;
}
in_();
if(!mark) continue;
if(t=='I')
{
ans2++;
if(cnt==3)
{
f1=getbe(k[1]),f2=getbe(k[2]);
if(f1!=f2)//不在一个集合中,合并,若该集合所有节点已知,即有根节点为n,则n做合并后的根节点
{
if(f1==n) addbe(k[2],f2,k[1],f1,k[3]);//有n就n做根节点
else addbe(k[1],f1,k[2],f2,k[3]);
}
else if((val[k[1]]^val[k[2]])!=k[3]) //若在同一集合,num[x]^num[y]=val[x]^val[fx]^val[y]^val[fy]=val[x]^val[y]
{
printf("The first %d facts are conflicting.\n",ans2);
mark=false;
}
}
else if(cnt==2)
{
f1=getbe(k[1]);
if(f1!=n)//改点未知,改为已知,加入n集合中
{
be[f1]=n;
val[f1]=k[2]^val[k[1]];//val[fx]=num[fx]=num[x]^val[x]
}
else if(val[k[1]]!=k[2])
{
printf("The first %d facts are conflicting.\n",ans2);
mark=false;
}
}
}
else if(t=='Q')
{
for(j=0;j<=n;j++) cn[j]=0;
int ans=0;
bool flag=true;
for(j=2;j<=cnt;j++)
{
f1=getbe(k[j]);
if(f1!=n) cn[f1]++;
ans^=val[k[j]];
}
for(j=0;j<n;j++)
if(cn[j]&1)
{
puts("I don't know.");
flag=false;
break;
}
if(flag) printf("%d\n",ans);
}
}
}
}