【题解】 CF1486C2 Guessing the Greatest (hard version)

题面传送门

解决思路

看到 \(1e5\) 的范围和 \(20\) 次询问,容易想到二分。

所以考虑如何来二分。

首先,可以询问一次 \([1,n]\),得到所有数中的次小值位置 \(x\) ,然后可以问一次 \([1,x]\) 得出最大值在 \(x\) 的左侧还是右侧。

然后再确定的区间里进行二分。以左边为例,初始 \(l=1,r=x\),每次询问 \([mid,x]\),若次大值仍是 \(x\),则说明最大值在 \([mid,x]\) 区间内,将 \(l\) 更新,反之则在另一边,将 \(r\) 更新。右边同理,每次询问每 \([x,mid]\) 即可。同时,每当 \(x\) 仍是次大值时,将 \(ans\) 更新成 \(mid\),最后得到的就是答案了。

注意一个细节:当第一次询问结果为 \(1\) 时,可以直接判定最大值在右区间,否则询问 \([1,1]\) 会出错。

AC Code

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
using namespace std;
int n,l,r,x,y,mid,ans;
bool fl;
void binary(int l,int r,int typ){
	while(l<=r){
		mid=(l+r)>>1;
		int _1=min(x,mid),_2=x+mid-_1;
		cout<<"? "<<_1<<" "<<_2<<endl;
		cin>>y;
		if((x==y)^typ) l=mid+1;   //两种情况放一起了
		else r=mid-1;
		if(x==y) ans=mid;  //更新ans
	}
}
signed main(){
	cin>>n;
	cout<<"? 1 "<<n<<endl;
	cin>>x;
	if(x!=1){
		cout<<"? 1 "<<x<<endl;
		cin>>y;
		if(y!=x) fl=1;
	}
	else fl=1;
	if(fl) binary(x+1,n,1);
	else binary(1,x-1,0);
	cout<<"! "<<ans<<endl;
	return 0;
}
posted @ 2022-10-14 09:53  Binary_Lee  阅读(26)  评论(0)    收藏  举报
Title