CF1354G Find a Gift

題面c++

英文題面git

題意:有點長,去洛谷上看吧~spa

題解:首先挖掘題目的性質:發現若是兩個集合都只有一個元素,且詢問的結果不是EQUAL,那麼小的那個必定就是禮物。那麼咱們但願能找到一個石頭,這樣判斷起來就能方便不少。code

考慮隨意指定30個位置\(p(p\neq 1)\),依次詢問\((1,p)\)。若是有返回SECOND的,那麼1就必定是禮物。發現因爲禮物個數少於\(\frac{n}{2}\),每次詢問位置\(p\)都有至少\(\frac{1}{2}\)的機率是石頭,那麼若是在30次詢問後一直沒有SECOND,那麼咱們能夠判定1是個石頭。考慮只有當這30次詢問的位置上都是禮物,且權值都比1小時纔有可能犯錯,所以這樣的犯錯機率是小於\(\frac{1}{2^{30}}\)的,能夠忽略。ci

當咱們肯定了一個石頭後,咱們就能夠進行倍增了。每次詢問\([1,2^{k-1}]\)\([2^{k-1}+1,2^k]\),若是返回EQUAL,那麼繼續倍增;若是是FIRST,那麼區間\([2^{k-1}+1,2^k]\)中就必定有禮物。經過二分查找,咱們就能找到最靠左的那個禮物的位置。get

總查詢次數是\(30+2logn\)的,恰好50次。it

代碼:class

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
mt19937 rnd(time(0));
char c[10];
int T,n,m,ans,v[1010],p;
IN Rand(int Mod){
	re res=rnd();if(res<0)res=-res;res%=Mod;return res;
}
I getit(int x,int y,int len){
	if(x==y)return cout<<"! "<<x<<endl,void();
	re mid=(x+y)>>1,ln=(len+1)>>1;
	cout<<"? "<<ln<<" "<<ln<<endl;
	F(i,x-ln,x-1)cout<<i<<" ";cout<<endl;
	F(i,x,mid)cout<<i<<" ";cout<<endl;
	cout.flush();cin>>c+1;if(c[1]=='W')exit(0);
	if(c[1]=='F')getit(x,mid,ln);
	else getit(mid+1,y,len-ln);
}
int main(){
	srand(time(0));
	cin>>T;
	while(T--){
		cin>>n>>m;C(v,0);ans=0;
		F(i,1,min(30,n-1)){
			while(1){
				p=Rand(n-1)+2;if(!v[p])break;
			}
			v[p]=1;
			cout<<"? 1 1"<<endl<<"1"<<endl<<p<<endl;cout.flush();
			cin>>c+1;if(c[1]=='W')return 0;
			if(c[1]=='S'){ans=1;break;}	
		}
		if(ans){cout<<"! "<<ans<<endl;cout.flush();continue;}
		re r=1,mid;
		while((r<<1)<=n){
			r<<=1;mid=r>>1;cout<<"? "<<mid<<" "<<mid<<endl;
			F(i,1,mid)cout<<i<<" ";cout<<endl;
			F(i,mid+1,r)cout<<i<<" ";cout<<endl;
			cout.flush();cin>>c+1;if(c[1]=='W')return 0;
			if(c[1]=='F'){
				getit(mid+1,r,mid);ans=1;
				break;
			}
		}
		if(ans)continue;
		getit(r+1,n,n-r);
	}
	return 0;
}
相關文章
相關標籤/搜索