@xiaoziyao
2021-08-18T06:30:49.000000Z
字数 1632
阅读 1517
解题报告
P7824 「RdOI R3」毒水解题报告:
瓶水,其中一瓶有毒,你可以检测若干次,每次选择若干瓶水,检测其中是否有毒水,但有且仅有一次检测结果会反过来,你需要在 次检测内找出毒水。注意,结果不是实时得到,当你询问结束后才能一次性得到所有结果。
()
交互杀我!!!
:很容易把每一瓶水检验三次,只要有两次检验出来就一定是毒水。
:考虑没有那次反过来的检测如何求,我们可以直接检测每个二进制位是否有毒水,然后拼起来就好了,如果有那次反过来的检测,检测三次就可以避免。
:检验三次消耗过大,考虑快速检验二级制位。
我们用 次检测对二进制位的检测再次二进制拆分,然后再用 次检测作为新加入的检测的异或。
很容易发现如果反转不在新加入的 个检测,那么这 个检测的死亡数为偶数。于是如果其为偶数,我们可以直接找到反转在前 个检测中的位置,否则前 个检测可以直接算。
检测次数 ,符合要求。
#include<stdio.h>#include<bitset>#include<math.h>using namespace std;const int maxn=1005,maxk=15;int n,k,lg,lglg;int v[maxk];bitset<maxn>ans,b[maxk];int main(){scanf("%d%d",&n,&k);if(n==1&&k==0){puts("2"),fflush(stdout);puts("1"),fflush(stdout);return 0;}lg=log2(n-1)+1,lglg=log2(lg-1)+1;for(int i=0;i<lg;i++){for(int j=0;j<n;j++)if((j>>i)&1)b[i][j]=1;printf("1 %d ",b[i].count());for(int j=0;j<n;j++)if(b[i][j])printf("%d ",j+1);puts("");}for(int i=0;i<lglg;i++){for(int j=0;j<lg;j++)if((j>>i)&1)b[lg+i]^=b[j];printf("1 %d ",b[lg+i].count());for(int j=0;j<n;j++)if(b[lg+i][j])printf("%d ",j+1);puts("");}for(int i=0;i<lglg;i++)b[lg+lglg]^=b[lg+i];printf("1 %d ",b[lg+lglg].count());for(int i=0;i<n;i++)if(b[lg+lglg][i])printf("%d ",i+1);puts("\n2"),fflush(stdout);for(int i=0;i<=lg+lglg;i++)scanf("%d",&v[i]);int ok=0;for(int i=0;i<=lglg;i++)ok^=(v[lg+i]^1);if(ok==0){int pos=0;for(int i=0;i<lglg;i++){int ook=(v[lg+i]^1);for(int j=0;j<lg;j++)if((j>>i)&1)ook^=(v[j]^1);pos+=(ook<<i);}v[pos]^=1;}ans.set();for(int i=0;i<lg;i++)if(v[i]==0)ans&=b[i];for(int i=0;i<n;i++)if(ans[i]){printf("%d\n",i+1),fflush(stdout);break;}return 0;}
