CF1491F Magnets
题面传送门
题面里那个式子显然可以化成\((n1-s1)(n2-s2)\)
考虑如果我们知道了一个磁铁的磁极那么就可以用\(n\)次询问问出所有的状态。
题面里那个绝对值不超过\(n\)很难搞,这暗示我们每次询问必定有一边只能有一个,那么绝对值就不会超过\(n\)。
考虑维护一个集合\(S\),初始令\(S=\{1,2,\dots ,n\}\),每次从集合中拿出一个数\(x\)并把这个数删了,然后询问这个数和集合里的数的关系。
如果这个数发现是有磁极的,那么就可以去问出来剩下的所有,因为之前拿出的至多会有一个因为集合里的数和为\(0\)而误判成没有,所以直接对剩下的二分即可。
然而你发现这个东西要\(n+\lceil \log_2n\rceil\)的(雾
考虑把删除变成加入,初始令集合为空,那么可以发现第一次是不用问的,就省下了一次询问。就可以\(n+\lfloor \log_2n\rfloor\)了。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (3000+5)
#define M ((1<<16)+5)
#define K (1000+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int l,r,mid,cnt,T,n,x,Fl[N];
I void Solve(){
int i,j;scanf("%d",&n);for(i=1;i<=n;i++) Fl[i]=0;
for(i=n-1;i;i--){
cout<<"? 1 "<<n-i<<endl;cout<<i<<endl;for(j=i+1;j<=n;j++) cout<<j<<" ";cout<<endl;
cin>>x;if(x) break;
}Fl[i]=1;
for(j=i-1;j;j--) cout<<"? 1 1"<<endl<<i<<endl<<j<<endl,cin>>Fl[j];
l=i;r=n+1;while(l+1<r){mid=l+r>>1;cout<<"? "<<1<<" "<<n-mid+1<<endl;
cout<<i<<endl;for(j=mid;j<=n;j++) cout<<j<<" ";cout<<endl;cin>>x;
(x?l:r)=mid;
} Fl[l]=1;cnt=0;for(j=1;j<=n;j++) cnt+=(!Fl[j]);cout<<"! "<<cnt<<" ";for(j=1;j<=n;j++) !Fl[j]&&(cout<<j<<" ");cout<<endl;
}
int main(){
scanf("%d",&T);while(T--) Solve();
}