题解 P8849【『JROI-7』hibernal】
题意
$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~

浙公网安备 33010602011771号