解析
写了不少线段树上二分,原来树状数组上也是可以二分的
首先如果
a
i
>
i
a_i>i
ai>i,那必然无法删除,下面只考虑
a
i
<
=
i
a_i<=i
ai<=i的情况
本题试图离线不难想到,但我一开始总是按照刻板思维尝试按序移动左端点,结果根本没法做…
反过来想,移动右端点就可做多了
设
f
i
f_i
fi表示当前右端点为r的情况下,[i,r]最多可以删掉的数量
那么我们当把r移动到r+1时,只有
f
i
>
=
i
−
a
i
f_i>=i-a_i
fi>=i−ai时才会对
f
i
f_i
fi产生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);
#endif
mi[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
*/