传送门
首先假设前
i
i
i个数有
f
i
f_i
fi个可以被删除,那么
f
i
=
f
i
−
1
+
[
i
−
a
i
≥
0
o
r
f
i
≥
i
−
a
i
]
f_{i}=f_{i-1}+[i-a_i\ge 0\,or\,f_i\ge i-a_i]
fi=fi−1+[i−ai≥0orfi≥i−ai]。
证明:我们可以先删除前 i − 1 i-1 i−1个数中的 i − a i i-a_i i−ai个可删的数,此时刚好可以把 a i a_i ai删除,于是把 a i a_i ai删除以后再去删除前面剩下的可删除的数即可。
设最多长度为 l i m [ i ] lim[i] lim[i]的前缀被锁定(即无法操作)后能够保证 a i a_i ai仍然能被删除,考虑二分 l i m [ i ] lim[i] lim[i]的值,因为 l i m [ i ] lim[i] lim[i]小的时候 a i a_i ai能被删除的可能性更大。 c h e c k check check的时候考虑对于前 i − 1 i-1 i−1个数假设有 c c c个数 l i m lim lim大于等于 m i d mid mid,若 c ≥ i − a i c\ge i-a_i c≥i−ai那么我们就知道 l i m [ i ] ≥ c lim[i]\ge c lim[i]≥c。
由于需要查询区间大于等于 x x x的数个数,因此需要用到主席树,由于数字是 0 ∼ n − 1 0\sim n-1 0∼n−1,不妨加个一即可。
对于查询而言,我们只需要查询 [ x , n − y ] [x,n-y] [x,n−y]区间内满足 l i m ≥ x lim\ge x lim≥x的数有多少个即可。
int a[maxn],lim[maxn],t[maxn<<5],tot=0,root[maxn],l[maxn<<5],r[maxn<<5];
void build(int &rt,int tl,int tr){
rt=++tot;
if(tl==tr)return;
int mid=tl+tr>>1;
build(l[rt],tl,mid);
build(r[rt],mid+1,tr);
}
void add(int &rt,int pre,int tl,int tr,int x){
rt=++tot;
t[rt]=t[pre]+(x!=-1);l[rt]=l[pre],r[rt]=r[pre];
if(tl==tr)return;
int mid=tl+tr>>1;
if(x<=mid)add(l[rt],l[pre],tl,mid,x);
else add(r[rt],r[pre],mid+1,tr,x);
}
int qry(int L,int R,int tl,int tr,int x){
int sm=t[R]-t[L];
if(tl>=x)return sm;
int mid=tl+tr>>1,ans=0;
if(mid>=x)ans+=qry(l[L],l[R],tl,mid,x);
ans+=qry(r[L],r[R],mid+1,tr,x);
return ans;
}
int main(){
int n=rd(),q=rd();
build(root[0],1,n);
FOR(i,1,n+1){
int u=rd();
u=i-u;
if(u<0){
lim[i]=-1;
add(root[i],root[i-1],1,n,-1);
}
else{
int l=0,r=i-1,ans=-1;
while(l<=r){
int mid=l+r>>1;
if(qry(root[0],root[i-1],1,n,mid+1)>=u){
l=mid+1;
ans=mid+1;
}else r=mid-1;
}
lim[i]=ans;
add(root[i],root[i-1],1,n,lim[i]);
}
}
while(q--){
int x=rd(),y=rd();
x++;
int ans=qry(root[x-1],root[n-y],1,n,x);
wrn(ans);
}
}