您的位置:首页 > 其它

【趣题&输入略坑】【并查集(带权)】NKOJ 3762 守夜人

2016-09-19 23:40 169 查看
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
=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);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐