有个数组(a_iin{-1,0,1})。你需要输出所有(a_i=0)的(i)。
交互。每次可以在这个数组的下标中选择两个不可相交的非空集合(S,T),询问(sum_{xin S}a_xsum_{yin T} a_y)。
询问次数(n+lfloorlg n floor)
(nle 2000)
保证存在至少一个(a_i=0),至少存在两个(a_i eq 0)。
本来在想着算出(x_i=query({a_i},U setminus {a_i})),然后后面进行一波分类讨论乱搞。感觉快行了但是剩下一种情况做不出来((sum a_iin {1,-1})时)。顺便提一下如果没有这种情况是(n)次的(手动滑稽)。
一开始把(a_1)丢入(S)中。然后从(2)开始做,先将(a_i)丢入(T)中问一下,如果问出了(0)则把(a_i)丢入(S)中;否则此时(a_i)一定非零,且(S)中的数的和也非零,因此此时(a_i)是第二个非零数。
于是在左边二分出第一个非零数,右边剩下的一个个判断。
次数(n-1+lceil lg n ceil)。
using namespace std;
#include <bits/stdc++.h>
#define N 2005
int n;
vector<int> a,b;
int ask(){
printf("? %d %d
",a.size(),b.size());
for (int i=0;i<a.size();++i) printf("%d ",a[i]);printf("
");
for (int i=0;i<b.size();++i) printf("%d ",b[i]);printf("
");
fflush(stdout);
int x;scanf("%d",&x);
return x;
}
int ans[N],cnt;
int main(){
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
cnt=0;
a.clear(),b.clear();
a.push_back(1);
for (int i=2;i<=n;++i){
b.push_back(i);
int x=ask();
if (!x){
b.pop_back();
a.push_back(i);
continue;
}
int l=1,r=i-1;
while (l<r){
int mid=l+r>>1;
a.clear();
for (int i=l;i<=mid;++i)
a.push_back(i);
x=ask();
if (x)
r=mid;
else
l=mid+1;
}
for (int j=1;j<i;++j)
if (j!=l)
ans[++cnt]=j;
for (int j=i+1;j<=n;++j){
a.clear();
a.push_back(j);
x=ask();
if (!x)
ans[++cnt]=j;
}
break;
}
printf("! %d ",cnt);
for (int i=1;i<=cnt;++i)
printf("%d ",ans[i]);
printf("
");
fflush(stdout);
}
return 0;
}