线段树维护区间最长连续0的长度

原理

本身区间最长连续0之间不可以进行区间加法操作,违背线段树基本的运算。但是容易想到两个区间合并产生的最值变化,主要由Lson的右端连续0长度加上Rson的左端连续0长度引起。即 tree[x].ma=max(tree[Lson].R0+tree[Rson].L0,max(tree[Lson].ma,tree[Rson].ma));
因此我们可以考虑维护一个区间的左端连续0个数和右端连续0个数。
但是在区间合并时我们会发现一个新的问题,即如果左区间全是0,那么tree[x].L0可以向右继续合并,tree[x].R0同理;
tree[x].L0=tree[Lson].L0+(左区间全是0?tree[Rson].L0:0);
tree[x].R0=tree[Rson].R0+(右区间全是0?tree[Rson].R0:0);

查询的时候同样需要注意不能简单返回左右区间的最大值,而是需要考虑左右区间是否存在全为零的情况;

代码

include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define L long long int
#define ls x<<1
#define rs x<<1|1
#define maxn 100005
#define maxm 1000005
int n,m;
int a[maxn];
///---------------------------------------------
///求区间最大连续0的个数  带区间修改和区间查询
struct node{
    int l0,r0,ma0;
    bool isall0;
    node(int l0,int r0,int ma0,bool isall0):l0(l0),r0(r0),ma0(ma0),isall0(isall0){};
    node(){};
};
node tree[maxn*4];
bool lazy[maxn];
void pushup(int x)
{
    tree[x].l0=tree[ls].l0+(tree[ls].isall0 ? tree[rs].l0 : 0);
    tree[x].r0=tree[rs].r0+(tree[rs].isall0 ? tree[ls].r0 : 0);
    tree[x].ma0=max(tree[ls].r0+tree[rs].l0,max(tree[ls].ma0,tree[rs].ma0));
    tree[x].isall0=tree[ls].isall0&tree[rs].isall0;
}
void pushdown(int x,int l,int r)
{
    int mid=(l+r)/2;
    tree[ls].l0=tree[ls].r0=tree[ls].ma0=mid-l+1;
    tree[rs].l0=tree[rs].r0=tree[rs].ma0=r-mid;
    tree[ls].isall0=tree[rs].isall0=1;
    lazy[ls]=lazy[rs]=1;
    lazy[x]=0;
}
void build(int x,int l,int r)
{
    if(l==r){
        tree[x].l0=tree[x].r0=tree[x].ma0=tree[x].isall0=a[l]^1;
    }
    else {
        int mid=(l+r)/2;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(x);
    }
}
void update(int x,int l,int r,int ll,int rr)
{
    if(lazy[x])pushdown(x,l,r);
    if(ll<=l&&r<=rr){
        tree[x].l0=tree[x].r0=tree[x].ma0=r-l+1;
        tree[x].isall0=1;
        lazy[x]=1;
        return ;
    }
    else {
        int mid=(l+r)/2;
        if(mid>=ll)update(ls,l,mid,ll,rr);
        if(mid+1<=rr)update(rs,mid+1,r,ll,rr);
        pushup(x);
    }
}
node query(int x,int l,int r,int ll,int rr)
{
    if(lazy[x])pushdown(x,l,r);
    if(ll<=l&&r<=rr)return tree[x];
    else{
        int mid=(l+r)/2;
        node now;
        now.isall0=now.l0=now.ma0=now.r0=0;
        if(rr<=mid)return query(ls,l,mid,ll,rr);
        else if(mid+1<=ll)return query(rs,mid+1,r,ll,rr);
        else if(ll<=mid&&mid+1<=rr){
            node t1=query(ls,l,mid,ll,rr);
            node t2=query(rs,mid+1,r,ll,rr);
            now.l0=t1.l0+(t1.isall0 ? t2.l0:0);
            now.r0=t2.r0+(t2.isall0 ? t1.r0:0);
            now.ma0=max(t1.r0+t2.l0,max(t1.ma0,t2.ma0));
            now.isall0=t1.isall0&t2.isall0;
            return now;
        }
    }
}
///------------------------------------------------------
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
    }
    build(1,1,n);
    for(int i=1,x,y,z;i<=m;i++){
        scanf("%d",&x);
        if(x==1){
            scanf("%d%d",&y,&z);
            update(1,1,n,y,z);
        }
        else {
            scanf("%d%d",&y,&z);
            node ans=query(1,1,n,y,z);
            printf("l0 = %d\tr0 = %d\tma0 = %d\n",ans.l0,ans.r0,ans.ma0);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值