P8849 题解
一道好题。
观察数据,要求询问次数不超过
我刚开始也在想二分,但这个东西并不具有单调性,但这个题具有的特点就是你不仅仅可以询问一个前缀,你还可以询问任意的集合。
首先发现如果能将
怎么解决呢?我们假设去分
随便取一个
但是我们暂时没有想到合适的方法能够用
所以我想到了在求
这里需要用到二进制分组的技巧啦!
从高到低地考虑每一个二进制位,将所有的位置按照这一位二进制位分为两个部分:一是这一位是
我们来看看能够得到些什么,如果返回
如果是
这样一定能得到一组询问答案是
这样就花去了
那另一个呢?我们不是求出金苹果两个每一个二进制位是否相同了吗?所以也解决了。其实熟练的人看一下就知道了那个是异或值,所以假如知道了
超短代码:
#include <iostream>
#define For(i, a, b, j) for (int i = (a); i <= (b); i = (j) )
using namespace std;
int T, n, k, kx;
int x, oplus;
int a[505], b[505];
int find (int l, int r) {
if (l == r) return b[l];
int mid = l + r >> 1;
printf ("? %d ", mid - l + 1);
For (i, l, mid, i + 1) printf ("%d ", b[i]);
printf ("\n");
fflush (stdout);
scanf ("%d", &x);
if (x == 1) return find (l, mid);
return find (mid + 1, r);
}
int main () {
fflush (stdout);
scanf ("%d", &T);
while (T --) {
oplus = 0;
fflush (stdout);
scanf ("%d", &n);
For (m, 1, n, m << 1) {
k = 0;
For (i, 1, n, i + 1) if ( (i & m) == 0) a[++ k] = i;
printf ("? %d ", k);
For (i, 1, k, i + 1) printf ("%d ", a[i]);
printf ("\n");
fflush (stdout);
scanf ("%d", &x);
oplus += x * m;
if (x == 1) {
For (i, 1, k, i + 1) b[i] = a[i];
kx = k;
}
}
int ans = find (1, kx);
printf ("! %d %d\n", min (ans, ans ^ oplus), max (ans, ans ^ oplus) );
fflush (stdout);
}
return 0;
}