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

32 篇文章 0 订阅
3 篇文章 0 订阅

题目大意:有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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值