方法一:
记忆化二分,有这么两种二分:
第一种和线段树的所有区间重叠,的子区间都是,如果开个桶去记忆化,需要查次,但可以知道的是被遍历的区间远小于这么多,所以可以去记忆化。

方法二:
扩大到的幂次,然后预处理的幂次的下标的值(即对原数组分块)。然后每次二分答案在那个块里面(已经预处理,不需要询问测评姬),接着只要在长度不大与的块里面二分询问就可以了。找到答案后用树状数组维护的值增加的值。

MyCode:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
map<pair<int,int>,int>mp;
void add(int l,int r,int x) {
    --mp[{l,r}];
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) add(l,mid,x);
    else add(mid+1,r,x);
}
int main() {
    int n,t,k;
    cin>>n>>t;
    while(t--) {
        cin>>k;
        int l=1,r=n,mid,x;
        while(l<r) {
            mid=(l+r)>>1;
            if(!mp.count({l,mid})) {
                cout<<"? "<<l<<' '<<mid<<endl;
                cin>>x;
                mp[{l,mid}]=mid-l+1-x;
            }
            x=mp[{l,mid}];
            if(k<=x) r=mid;
            else {
                l=mid+1;
                k-=x;
            }
        }
        cout<<"! "<<r<<endl;
        add(1,n,r);
    }
    return 0;
}