题解 P8849【『JROI-7』hibernal】

$\text{Link}$

题意

$n$ 个物品,每个物品有编号,有且仅有两个特殊物品。每次询问你可以将物品划分为两堆,交互库会告诉你两堆里特殊物品的数量的乘积。

你需要用不超过 $19$ 次询问求出这两个特殊物品的编号。多组数据。

$n\le1000$,$T\le200$

思路

不同于官方题解的做法。

设答案为 $x,y$。

考虑对于每一位 $i$,将编号第 $i$ 位为 $0$ 的放到 $S_0$,其余放到 $S_1$,查询 $S_0,S_1$,可以得到 $x,y$ 这一位是否相同。换个说法,我们通过 $\log n$ 次询问得到了 $x\text{ xor }y$。

指定一位 $w$,$x,y$ 在第 $w$ 位不同,不妨指定 $x$ 第 $w$ 位为 $1$。对于每一位 $i\ne w$,把编号第 $i$ 位为 $1$ 且第 $w$ 位为 $1$ 的放到 $S_0$,其余放到 $S_1$,查询 $S_0,S_1$。通过这次询问,我们可以求出 $x$ 这一位是否为 $1$,回答为 $0$ 时,$x$ 在 $S_1$ 中,说明其第 $i$ 位为 $0$,反之同理。这里有 $\log n-1$ 次询问。

综上,我们通过 $2\log n-1$ 次询问求出了 $x$ 和 $x\text{ xor }y$,输出时要注意 $x<y$。

时间复杂度 $O(Tn\log n)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e3+10;
inline int query(vector<int>qry){
    printf("? %d ",qry.size());
    for(auto k:qry)
        printf("%d ",k);
    puts("");
    fflush(stdout);
    int x;
    scanf("%d",&x);
    return x;
}
int T;
int main(){
    scanf("%d",&T);
    while(T--){
        int n,xt=0,p,x=0;
        scanf("%d",&n);
        for(int i=0;(1<<i)<=n;i++){
            vector<int>a;
            a.clear();
            for(int j=1;j<=n;j++)
                if(j&(1<<i))
                    a.push_back(j);
            if(!a.size()||a.size()==n) continue;
            int tmp=query(a);
            if(tmp) xt|=1<<i,p=i;
        }
        x|=1<<p;
        for(int i=0;(1<<i)<=n;i++){
            if(i==p) continue;
            vector<int>a;
            a.clear();
            for(int j=1;j<=n;j++)
                if((j&(1<<i))&&(j&(1<<p)))
                    a.push_back(j);
            int tmp;
            if(!a.size()||a.size()==n) tmp=0;
            else tmp=query(a);
            x|=tmp<<i;
        }
        if((xt^x)<x) x=xt^x;
        printf("! %d %d\n",x,xt^x);
        fflush(stdout);
    }
    flush();
}

再见 qwq~

posted @ 2022-11-15 09:38  ffffyc  阅读(5)  评论(0)    收藏  举报  来源