交互、贪心、二分

发现这个询问只能鉴定是否 $S_1,S_2$ 都有金苹果。

考虑按位询问,每次询问编号第 $i$ 位为 $0$ 的所有苹果和第 $i$ 位为 $1$ 的所有苹果,这样能通过至多 $10$ 次询问得到两个金苹果编号的异或和。

接下来找到异或和一个为 $1$ 的位(显然由于两个金苹果编号不同所以必定有一个位是 $1$),这一位为 $0$ 的所有苹果里肯定有一个金苹果,可以使用这个金苹果鉴定一个集合里是不是有金苹果。每次鉴定一半的可疑苹果可以排除掉一半的可疑苹果,最后剩余的可疑苹果就是金苹果。

由于某一位为 $1$ 的金苹果至多有 $512$ 个,不超过 $9$ 次询问就可以得到金苹果的编号,异或得到另一个金苹果编号。询问次数最多为 $19$ 次,可以通过本题。

#include<bits/stdc++.h>
using namespace std;
int ask(set<int> s){
    static int ret;
    printf("? %u",s.size());
    for(int i:s) printf(" %d",i);
    putchar('\n');
    fflush(stdout);
    scanf("%d",&ret);
    return ret;
}
void solution(){
    int n=0,w=0,sum=0,ans=0;
    scanf("%d",&n);
    for(int i=0;(1<<i)<=n;i++){
        set<int> s;
        for(int j=1;j<=n;j++) if((j>>i)&1) s.emplace(j);
        if(ask(s)) w=i,sum|=1<<i;
    }
    set<int> res;
    for(int i=1;i<=n;i++) if((i>>w)&1) res.emplace(i);
    while(res.size()>1u){
        set<int> s[2];
        int x=0;
        for(int i:res) s[x^=1].emplace(i);
        res=s[ask(s[1])];
    }
    ans=*res.begin();
    if(ans>(ans^sum)) ans^=sum;
    printf("! %d %d\n",ans,ans^sum);
    fflush(stdout);
}
int T;
int main(){
    scanf("%d",&T);
    while(T--) solution();
    return 0;
}
最后修改:2024 年 10 月 03 日
如果觉得我的文章对你有用,请随意赞赏