Codeforces 835E. The penguin's game(思维题)
http://codeforces.com/problemset/problem/835/E
题意:
这是一道交互题
有n个数,其中有2个y,n-2个x
每次你可以询问若干个数的异或和,从而得出y的位置
最开始给出n,x,y
最多询问19次
多猪试毒问题:
有1000瓶药,其中只有1瓶是解药,其他的都是毒药
你有一些猪,今天让他们会喝下若干瓶药,所有没有喝到解药的猪会在明天的同一时刻死去
问最少需要多少头猪一定会试出哪瓶是解药
答案是10头
因为不同的数的二进制位至少有一位不相同
10位二进制可表示1000以内的所有的数
让第i头猪喝下所有第i位二进制位为1的药
如果明天第i头猪没有死,就可以确定解药的第i位二进制位为1
这道题相当于是有两瓶解药,而且如果喝下两瓶解药会变成毒药
让你构造一种喝药的方案,用至多19头猪找出解药
如果我们能想办法找到一堆药,使得这里面有且只有一瓶解药,那就可以套用上面的方法了
同样的方法,两瓶解药的二进制位至少有一位不同
如果有一头猪没有死,那么它所喝下的药里一定只有一瓶解药,它没喝的药里面也有且仅有一瓶解药
最多用10头猪就可以找到这样的一头猪
它喝的药 和 没喝的药 数量少的那一组里套用一次上面的多猪试毒的方法
就可以找到数量少的那一组 的解药的编号
这一步最多用9头猪
注意一定要用数量少的那一组,这样才能使数量至少减半,变成2^9数量级
(⊙o⊙)…,现在没有猪了,还有一瓶解药,怎么办?
回到第一步,如果有一头猪死了,代表它要么喝下了两瓶解药,要么没喝解药,说明两瓶解药的二进制位相同
如果有一头猪没死,说明两瓶解药的二进制位不同
记录下那些二进制位不同
异或上第一瓶解药的编号即可得到第二瓶解药的编号
在这道题里,
解药相当于y,
毒药相当于x,
猪喝下某些药死了相当于询问的位置异或和为 0 或者 x
猪喝下某些药没死相当于询问的位置异或和为 y 或者 x xor y
#include<cstdio> #include<vector> #include<cstring> using namespace std; vector<int>V,U; bool vis[1001]; int main() { int n,x,y; scanf("%d%d%d",&n,&x,&y); int siz; int tmp; bool tag=false; int bit=0; int pos; int ans=0; for(int i=0;i<=9;++i) { V.clear(); if(!tag) memset(vis,false,sizeof(vis)); for(int j=1;j<=n;++j) if(j&(1<<i)) V.push_back(j),vis[j]=true; siz=V.size(); if(!siz) continue; printf("? %d ",siz); for(int j=0;j<siz;++j) printf("%d ",V[j]); printf("\n"); fflush(stdout); scanf("%d",&tmp); if(tmp==y || tmp==(x^y)) { bit+=1<<i; if(!tag) { tag=true; pos=i; if(siz<n-siz) U=V,ans=1<<i; else { for(int j=1;j<=n;++j) if(!vis[j]) U.push_back(j); } } } } int m=U.size(); for(int i=0;i<=9;++i) { if(pos==i) continue; V.clear(); for(int j=0;j<m;++j) if(U[j]&(1<<i)) V.push_back(U[j]); siz=V.size(); if(!siz) continue; printf("? %d ",siz); for(int j=0;j<siz;++j) printf("%d ",V[j]); printf("\n"); fflush(stdout); scanf("%d",&tmp); if(tmp==y || tmp==(x^y)) ans+=1<<i; } printf("! %d %d",min(ans,ans^bit),max(ans,ans^bit)); }