这是我所做不出来的题目。我什么时候才能 GM 啊?让我回个 IM 也好嘛!
洛谷题目页面传送门 & CF 题目页面传送门
题意见洛谷。
首先探索一个下标序列能够全部被删除的充要条件。我们设 (b_i=i-a_i),那么任意时刻,显然 (a_i) 能被删掉当且仅当 (b_i=0)。于是我们盯着这个 (b) 看:一开始肯定要有一个先驱者 (b_i=0) 对不对,然后删除之后就可以让后面的 (b) 值都减去 (1)。一个 (b_i),如果它 (<0) 那显然没救了;如果 (geq 0) 那么只需要将它减去 (b_i) 次 (1) 就变成 (0) 可以被删掉了。也就是说一个 (b_igeq 0) 的位置 (i) 要被删掉必须满足前面有 (b_i) 个已经被删掉了。于是我们得到了一个必要条件:下标序列 (c) 能被全部删掉仅当 (c_ileq i-1)。
然后随便 van van 发现这个也满足充分性的,只需要构造出一个方案即可证明。那么方案很好构造,就每次删掉 (c) 中最右边满足 (b_{c_i}=0) 的 (c_i) 即可。
然后再对询问进行一番显然的转化:每组询问 (x,y) 相当于求 (b) 上的区间 ([l=x+1,r=n-y]) 的最长能被 ([0,1,2,3,cdots]) 所「覆盖」的子序列长度。就变成了一个看上去很清新的 DS 题。
如何求呢??
暂时想不到直接对区间维护的,不妨假设区间左端点固定,来求(这个其实是老套路了)。那么可以维护以每个位置为最后一位的最长子序列长度 (-1),这是一个 (mathrm O(n)) 的数列对吧。如何求这个数列很容易,类似 DP 的,当前位置 (i) 答案为 (x) 要满足两个条件:
- 前面有 (x-1) 长度的,即 (maxlimits_{j=1}^{i-1}{dp_j}geq x-1);
- 当前这位要满足,即 (xgeq a_i)。
综合起来就是 (xinleft[a_i,maxlimits_{j=1}^{i-1}{dp_j}+1 ight])。要取最长的,于是如果区间不为空就取右端点,如果为空就设一个错误值,比如 (-infty) 吧(可能会比较方便)。然后如果光是左端点固定的话,这个很显然是可以 (mathrm O(n)) 的。可惜固定不得。
于是考虑从右往左枚举左端点,相当于依次 push_front 然后维护整个 DP 数组(跟上面套路配套的套路)。考虑如何维护。
显然任意时刻 DP 数组是在 ([0,1,2,3,cdots]) 里面插若干个 (-infty) 这个形式。然后发现在往前面 push 的过程中,push 的是 (0),所以这个自然数序列要往后顺移,即全体加 (1)。然后所有 (-infty) 当年所对应的区间也在加 (1),慢慢加的话可能有朝一日能使那个区间不为空,重获新生。又显然最多会有 (n) 次重获新生的说,所以这个就暴力修改就可以了(每个时刻从前往后依次修改,每次修改还要附带后缀加 (1))。那么如何每次快速找到复活的人们呢?而且不仅要找到,还要每次找到的是最前面的?维护一个线段树,然后线段树二分即可。
最后,这个「老套路」离线 + 线段树实现是比较方便的,但是注意到每次是在前一个历史版本上直接修改,也可以用主席树预处理来实现回答询问的在线,不过我不会,而且也很简单的样子,不管了。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int inf=0x3f3f3f3f;
const int N=300000,QU=N;
int n,qu;
int a[N+1];
struct query{int l,r;}qry[QU+1];
vector<int> pos[N+1];
int ans[QU+1];
struct segtree{
struct node{int l,r,dif,rl,lz;}nd[N<<2];
#define l(p) nd[p].l
#define r(p) nd[p].r
#define dif(p) nd[p].dif
#define rl(p) nd[p].rl
#define lz(p) nd[p].lz
void bld(int l=1,int r=n,int p=1){
l(p)=l;r(p)=r;dif(p)=rl(p)=-inf;lz(p)=0;
if(l==r)return;
int mid=l+r>>1;
bld(l,mid,p<<1);bld(mid+1,r,p<<1|1);
}
void init(){bld();}
void sprup(int p){dif(p)=max(dif(p<<1),dif(p<<1|1));rl(p)=max(rl(p<<1),rl(p<<1|1));}
void sprdwn(int p){
if(lz(p)){
dif(p<<1)+=lz(p);rl(p<<1)+=lz(p);lz(p<<1)+=lz(p);
dif(p<<1|1)+=lz(p);rl(p<<1|1)+=lz(p);lz(p<<1|1)+=lz(p);
lz(p)=0;
}
}
void chg(int x,int v1,int v2,int p=1){
if(l(p)==r(p))return dif(p)=v1,rl(p)=v2,void();
sprdwn(p);
int mid=l(p)+r(p)>>1;
chg(x,v1,v2,p<<1|(x>mid));
sprup(p);
}
void add(int l,int r,int v,int p=1){
if(l>r)return;
if(l<=l(p)&&r>=r(p))return dif(p)+=v,rl(p)+=v,lz(p)+=v,void();
sprdwn(p);
int mid=l(p)+r(p)>>1;
if(l<=mid)add(l,r,v,p<<1);
if(r>mid)add(l,r,v,p<<1|1);
sprup(p);
}
int mx(int l,int r,int p=1){
if(l<=l(p)&&r>=r(p))return rl(p);
sprdwn(p);
int mid=l(p)+r(p)>>1,res=-inf;
if(l<=mid)res=max(res,mx(l,r,p<<1));
if(r>mid)res=max(res,mx(l,r,p<<1|1));
return res;
}
int fst(){
int p=1;
if(dif(p)<0)return 0;
while(l(p)<r(p)){
sprdwn(p);
if(dif(p<<1)>=0)p=p<<1;
else p=p<<1|1;
}
return l(p);
}
}segt;
int main(){
cin>>n>>qu;
for(int i=1;i<=n;i++)scanf("%d",a+i),a[i]=i-a[i];
for(int i=1;i<=qu;i++)scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].l++,qry[i].r=n-qry[i].r,pos[qry[i].l].pb(i);
segt.init();
for(int i=n;i;i--){
if(a[i]==0){
segt.chg(i,-inf,0);segt.add(i+1,n,1);
int fd;
while(fd=segt.fst()){
segt.chg(fd,-inf,segt.mx(1,fd-1)+1);segt.add(fd+1,n,1);
}
}
else if(a[i]>0)segt.chg(i,-a[i],-inf);
for(int j=0;j<pos[i].size();j++)ans[pos[i][j]]=max(0,segt.mx(1,qry[pos[i][j]].r)+1);
}
for(int i=1;i<=qu;i++)printf("%d
",ans[i]);
return 0;
}