CF835E The penguin's game(异或性质+二分查找)

来源:互联网 发布:你睡过几个男人 知乎 编辑:程序博客网 时间:2024/05/18 02:34

题目大意:有n个数,其中有且只有两个数为y,其他的都为x,让你找到这两个为y的位置pos1,pos2。你可以问20个问题,每个问题为你给出一个集合,系统回馈给你他们的异或和。首先,每个集合中要不只有1个y,要不有0或2个y。我们通过问一个问题就可以知道这个集合中y的个数是奇数还是偶数。分以下四种情况:
1、集合大小为偶数,y有偶数个,则x有偶数个,那么返回值为0
2、集合大小为偶数,y有奇数个,则x有奇数个,那么返回值为x^y
3、集合大小为奇数,y有偶数个,则x有奇数个,那么返回值为x
4、集合大小为奇数,y有奇数个,则x有偶数个,那么返回值为y
因为x!=y,x!=0,y!=0,所以这四种返回值一定是不同的数。我们根据返回值就可以判断y的个数。
我们把每个数当做二进制,则最多有log2n+1我们按每一位上是1还是0分组。每次把所有数分成两组。总共分log2n+1次。每次我们把这位(0..log2n)上是1的放到a里,询问a这个子集中y的个数,如果有一个,那么显然pos1,pos2的这一位是不同的。把这种不同的位记下来,记作differ。最后找一个最小的只含1个y的子集,记这个子集的大小为m,显然m<=n/2,在这个子集里二分查找这个y的位置,需要询问log2m次,记作pos,那么另一个位置就是pos^differ。最多问19次。

#include <cstdio>#include <cstring>#include <iostream>#include <vector>#include <algorithm>using namespace std;int n,x,y,differ=0,differbit=-1;vector<int> a;vector<int> b;int ask(){    if(a.empty()) return 0;    printf("? %d",a.size());    for(int i=0;i<a.size();++i)        printf(" %d",a[i]);    puts("");fflush(stdout);    int res;    scanf("%d",&res);    return res;}int solve(){//b这个子集中只有一个特殊值,用二分找到它。     int l=0,r=b.size()-1;    while(l<r){        int mid=(l+r)>>1;a.clear();        for(int i=l;i<=mid;++i) a.push_back(b[i]);        int res=ask();        if(res==y||res==(x^y)) r=mid;        else l=mid+1;    }return b[l];}int main(){    scanf("%d%d%d",&n,&x,&y);    for(int i=0;(1<<i)<=n;++i){        a.clear();        for(int j=1;j<=n;++j)            if(j&(1<<i)) a.push_back(j);        int res=ask();        if(res==y||res==(x^y))  differ|=(1<<i),differbit=i;    }    a.clear();    for(int i=1;i<=n;++i){        if(i&(1<<differbit)) a.push_back(i);        else b.push_back(i);    }    if(a.size()<b.size()) swap(a,b);    int pos1=solve();    int pos2=pos1^differ;    if(pos1>pos2) swap(pos1,pos2);    printf("! %d %d\n",pos1,pos2);    return 0;}
原创粉丝点击