CF1491F. Magnets

有个数组(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;
}
原文地址:https://www.cnblogs.com/jz-597/p/14465768.html