CF1491F. Magnets

有个数组\(a_i\in\{-1,0,1\}\)。你需要输出所有\(a_i=0\)\(i\)

交互。每次可以在这个数组的下标中选择两个不可相交的非空集合\(S,T\),询问\(\sum_{x\in S}a_x\sum_{y\in T} a_y\)

询问次数\(n+\lfloor\lg n\rfloor\)

\(n\le 2000\)

保证存在至少一个\(a_i=0\),至少存在两个\(a_i\neq 0\)


本来在想着算出\(x_i=query(\{a_i\},U \setminus \{a_i\})\),然后后面进行一波分类讨论乱搞。感觉快行了但是剩下一种情况做不出来(\(\sum a_i\in \{1,-1\}\)时)。顺便提一下如果没有这种情况是\(n\)次的(手动滑稽)。

一开始把\(a_1\)丢入\(S\)中。然后从\(2\)开始做,先将\(a_i\)丢入\(T\)中问一下,如果问出了\(0\)则把\(a_i\)丢入\(S\)中;否则此时\(a_i\)一定非零,且\(S\)中的数的和也非零,因此此时\(a_i\)是第二个非零数。

于是在左边二分出第一个非零数,右边剩下的一个个判断。

次数\(n-1+\lceil \lg n\rceil\)


using namespace std;
#include <bits/stdc++.h>
#define N 2005
int n;
vector<int> a,b;
int ask(){
	printf("? %d %d\n",a.size(),b.size());
	for (int i=0;i<a.size();++i) printf("%d ",a[i]);printf("\n");
	for (int i=0;i<b.size();++i) printf("%d ",b[i]);printf("\n");
	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("\n");
		fflush(stdout);
	}
	return 0;
}
posted @ 2021-03-01 20:42  jz_597  阅读(47)  评论(0编辑  收藏  举报