解析
写了不少线段树上二分,原来树状数组上也是可以二分的
首先如果ai>ia_i>iai>i,那必然无法删除,下面只考虑ai<=ia_i<=iai<=i的情况
本题试图离线不难想到,但我一开始总是按照刻板思维尝试按序移动左端点,结果根本没法做…
反过来想,移动右端点就可做多了
设fif_ifi表示当前右端点为r的情况下,[i,r]最多可以删掉的数量
那么我们当把r移动到r+1时,只有fi>=i−aif_i>=i-a_ifi>=i−ai时才会对fif_ifi产生1的贡献
不难发现这个f是单调不升的,所以符合上面那个条件的f应该是一段前缀,可以利用二分求出
然后就是简单的对f区间+1,用树状数组维护即可
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+100;
const int mod=1e9+7;
#define ll long long
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();};while(isdigit(c)){x=x*10+c-'0';c=getchar();};return x*f;
}int n,m;int f[N],a[N],mi[20];
inline void add(int p,int v){for(;p<=n;p+=p&-p) f[p]+=v;return;
}
inline int ask(int p){int res(0);for(;p;p-=p&-p) res+=f[p];return res;
}
inline int find(int val){int res=0,pl=0;for(int k=18;k>=0;k--){if(pl+mi[k]>n||res+f[pl+mi[k]]<val) continue;pl+=mi[k];res+=f[pl];}return pl;
}
struct query{int l,r,id;bool operator < (const query oth)const{return r<oth.r;}
}p[N];
int ans[N];
int main(){#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);#endifmi[0]=1;for(int i=1;i<=18;i++) mi[i]=mi[i-1]<<1;n=read();m=read();for(int i=1;i<=n;i++) a[i]=i-read();for(int i=1;i<=m;i++) p[i]=(query){(int)read()+1,n-(int)read(),i};sort(p+1,p+1+m);int pl=1;//for(int i=1;i<=n;i++) printf("%d ",a[i]);//putchar('\n');for(int i=1;i<=n;i++){if(a[i]>=0){int pl=find(a[i]);//printf("i=%d pl=%d\n",i,pl);add(1,1);add(min(pl+1,i+1),-1);}while(pl<=m&&p[pl].r==i){ans[p[pl].id]=ask(p[pl].l);pl++;}}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);return 0;
}
/*
2 3
7 4 9 9
1 2 8
3 1
4 2 4
*/