BZOJ4491: 我也不知道题目名字是什么

考虑将原数列差分,那么最长不下降字串意味着最长的大于等于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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值