如果我们继续按照简单版的思路
也就是直接二分,那么次数会过多然后WA
如下
WA_CODE
#include <iostream>
using namespace std;
int query(int l,int r){
cout << '?' << ' ' << l << ' ' << r << endl;
int tmp;
cin >> tmp;
return tmp;
}
int main(){
int n,t;
cin >> n >> t;
while(t--){
int k;
cin >> k;
int l = 1,r = n;
int mid,tmp;
while(l<r){
mid = (l+r)>>1;
tmp = query(l,mid);
tmp = mid-l+1-tmp;
if(k>tmp){
l=mid+1;
k-=tmp;
}else{
r=mid;
}
}
cout << '!' << ' ' << l << endl;
}
return 0;
}
题解
次数过多的原因那必然是重复询问了;
那么我们可以用一个线段树来存储已经访问过的区间,用 v i s ( i ) vis(i) vis(i)来标识
同时二分的过程也丢给线段树来处理就行了;
Code
#include <iostream>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 2e5+10;
int tree[N<<2];//存储一段区间的总和
bool vis[N<<2];
int ask(int l,int r,int p){
if(vis[p]) return tree[p];
cout << '?' << ' ' << l << ' ' << r << endl;
int tmp;
cin >> tmp;
tree[p]=tmp;
vis[p]=1;
return tmp;
}
void push_up(int p){
if(vis[lc]&&vis[rc]){
tree[p]=tree[lc]+tree[rc];
vis[p]=1;
return;
}
//如果两个儿子不全 p又没访问过的话
//那么就等待ask吧
if(vis[p]) ++tree[p];
}
void query(int l,int r,int p,int k){
if(l==r){
tree[p]=vis[p]=1;
cout << "! " << l << endl;
return;
}
int mid = (l+r)>>1;
int tmp = ask(l,mid,lc);
tmp = mid-l+1-tmp;
if(k>tmp) query(mid+1,r,rc,k-tmp);
else query(l,mid,lc,k);
push_up(p);
}
int main(){
int n,t;
cin >> n >> t;
while(t--){
int k;
cin >> k;
query(1,n,1,k);
}
return 0;
}