【题解】 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;
}