考虑将原数列差分,那么最长不下降字串意味着最长的大于等于0的字串,然后就是经典思路了。
线段树记录每个区间从最左端开始最长的连续的1是lm,从右端开始最长的连续的1是rm,以及这个区间总的最长连续的1是am,两个区间合并的时候,总的最长的连续的1有左儿子的am,右儿子的am以及左儿子的rm+右儿子的lm更新即可。
注意差分后,如果询问l==r要特判。
有一个小技巧,由于原题要求最长不下降或者最长不上升,我们可以将原串reverse一遍见两颗线段树写在结构体里,这样就不用维护好多别的变量了…
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
//by:MirrorGray
using namespace std;
const int N=111111;
int a[N];
struct segment_tree{
int a[N];
struct seg{int l,r,lm,rm,am;}tree[N<<2];
inline seg merge(const seg&a,const seg&b){
seg ret;ret.l=a.l;ret.r=b.r;
if(a.am==a.r-a.l+1)ret.lm=a.am+b.lm;else ret.lm=a.lm;
if(b.am==b.r-b.l+1)ret.rm=b.am+a.rm;else ret.rm=b.rm;
ret.am=max(max(a.am,b.am),a.rm+b.lm);
return ret;
}
void build(int tr,int l,int r){
tree[tr].l=l;tree[tr].r=r;
if(l==r){
if(a[l]>=0)tree[tr].lm=tree[tr].rm=tree[tr].am=1;
else tree[tr].lm=tree[tr].rm=tree[tr].am=0;
return ;
}
int mid=(l+r)>>1;
build(tr<<1,l,mid);build(tr<<1|1,mid+1,r);
tree[tr]=merge(tree[tr<<1],tree[tr<<1|1]);
}
seg Q(int tr,int l,int r){
if(tree[tr].l==l&&tree[tr].r==r)return tree[tr];
int mid=(tree[tr].l+tree[tr].r)>>1;
if(l>mid)return Q(tr<<1|1,l,r);else if(r<=mid)return Q(tr<<1,l,r);
else return merge(Q(tr<<1,l,mid),Q(tr<<1|1,mid+1,r));
}
}t1,t2;
int main(){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<=n;i++)t1.a[i-1]=a[i]-a[i-1];
reverse(a+1,a+1+n);
for(int i=2;i<=n;i++)t2.a[i-1]=a[i]-a[i-1];
t1.build(1,1,n-1);t2.build(1,1,n-1);
int q;scanf("%d",&q);
while(q--){
int l,r;scanf("%d%d",&l,&r);
if(l==r){puts("1");continue;}
printf("%d\n",max(t1.Q(1,l,r-1).am,t2.Q(1,n-r+1,n-l).am)+1);
}
return 0;
}