【SHOI2015】脑洞治疗仪 题解 (线段树)

题目链接

题目大意:给定一个只含$0$和$1$的序列。有三种操作:1.把$[l,r]$内所有数改为$0$;2.把$[l,r]$内所有$1$拿走来填$[l',r']$内所有$0$。多了丢掉,少了优先从左开始填;3.查询$[l,r]$内最长的$0$串。

---------------------------

一眼能看出来考最大子段和。但是代码好难调啊QAQ

对于一段序列,我们要维护$4$个东西:$ls,rs,ms,s$,分别为从左端点开始最长的$0$串,从右端点开始最长的$0$串,序列内最长的$0$串,序列和。

维护最大子段和,无非就两种情况:跨过$mid$和没有跨过$mid$。注意在维护$ms$的时候有三种情况:左儿子的$ms$,右儿子的$ms$,左儿子$rs$加右儿子$ls$。也可以翻我之前的博客。

对于区间修改我们维护一个$lazy$,在$pushdown$的时候维护一下序列就好。

最后说一下$2$操作。我们注意到在一段序列内的和是非严格单调递增的。所以要确定我们填的这个区间,我们只需二分一下这个右端点即可。

然后就是恶心人的调代码时间了。压过行后也有130行……

时间复杂度$O(n\log^2 n)$

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int n,m;
struct node
{
    int ms,ls,rs,s,lazy,l,r,len;
}tree[maxn*4];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void pushup(int index)
{
    tree[index].s=tree[index*2].s+tree[index*2+1].s;
    if (tree[index*2].len==tree[index*2].ls)
        tree[index].ls=tree[index*2].len+tree[index*2+1].ls;
    else tree[index].ls=tree[index*2].ls;
    if (tree[index*2+1].len==tree[index*2+1].rs)
        tree[index].rs=tree[index*2+1].len+tree[index*2].rs;
    else tree[index].rs=tree[index*2+1].rs;
    tree[index].ms=max(max(tree[index*2].ms,tree[index*2+1].ms),tree[index*2].rs+tree[index*2+1].ls);
} 
inline void build(int index,int l,int r)
{
    tree[index].l=l;tree[index].r=r;
    tree[index].len=r-l+1;
    tree[index].lazy=-1;
    if (l==r){tree[index].s=1;tree[index].ls=tree[index].rs=tree[index].ms=0;return;}
    int mid=(l+r)>>1;
    build(index*2,l,mid);
    build(index*2+1,mid+1,r);
    pushup(index);
}
inline void pushdown(int index,int l,int r)//ms,ls,rs,s,lazy
{
    if (tree[index].lazy==0)
    {
        tree[index*2].lazy=tree[index*2+1].lazy=tree[index*2].s=tree[index*2+1].s=0;
        tree[index*2].ms=tree[index*2].ls=tree[index*2].rs=tree[index*2].len;
        tree[index*2+1].ms=tree[index*2+1].ls=tree[index*2+1].rs=tree[index*2+1].len;
    }
    if (tree[index].lazy==1)
    {
        tree[index*2].lazy=tree[index*2+1].lazy=1;
        tree[index*2].s=tree[index*2].len;
        tree[index*2+1].s=tree[index*2+1].len;
        tree[index*2].ms=tree[index*2].ls=tree[index*2].rs=0;
        tree[index*2+1].ms=tree[index*2+1].ls=tree[index*2+1].rs=0;
    }
    tree[index].lazy=-1;
}
inline void update(int index,int l,int r,int ql,int qr,int k)
{
    if (ql<=l&&r<=qr)
    {
        tree[index].s=tree[index].len*k;
        if (k==0) tree[index].ls=tree[index].rs=tree[index].ms=tree[index].len,tree[index].lazy=0;
        else tree[index].ls=tree[index].rs=tree[index].ms=0,tree[index].lazy=1;
        return;
    }
    pushdown(index,l,r);
    int mid=(l+r)>>1;
    if (ql<=mid) update(index*2,l,mid,ql,qr,k);
    if (qr>mid) update(index*2+1,mid+1,r,ql,qr,k);
    pushup(index);
}
inline int querysum(int index,int l,int r,int ql,int qr)
{
    if (ql<=l&&r<=qr) return tree[index].s;
    pushdown(index,l,r);
    int mid=(l+r)>>1,res=0;
    if (ql<=mid) res+=querysum(index*2,l,mid,ql,qr);
    if (qr>mid) res+=querysum(index*2+1,mid+1,r,ql,qr);
    return res;
}
inline int queryrest(int index,int l,int r,int ql,int qr)
{
    if (ql<=l&&r<=qr) return tree[index].len-tree[index].s;
    pushdown(index,l,r);
    int mid=(l+r)>>1,res=0;
    if (ql<=mid) res+=queryrest(index*2,l,mid,ql,qr);
    if (qr>mid) res+=queryrest(index*2+1,mid+1,r,ql,qr);
    return res;
}
inline int querymax(int index,int l,int r,int ql,int qr)
{
    if (ql<=l&&r<=qr) return tree[index].ms;
    pushdown(index,l,r);
    int mid=(l+r)>>1,res=0;
    if (tree[index*2].r<ql) return querymax(index*2+1,mid+1,r,ql,qr);
    else if (qr<tree[index*2+1].l) return querymax(index*2,l,mid,ql,qr);
    else{
        res=max(querymax(index*2,l,mid,ql,qr),querymax(index*2+1,mid+1,r,ql,qr));
        res=max(res,min(tree[index*2].rs,tree[index*2+1].l-ql)+min(tree[index*2+1].ls,qr-tree[index*2].r));
    }
    return res;
}
inline void work(int l0,int r0)
{
    int l1=read(),r1=read();
    int x=querysum(1,1,n,l0,r0);
    if (x==0) return;
    update(1,1,n,l0,r0,0);
    int l=l1,r=r1,ans;
    while(l<=r){
        int mid=(l+r)>>1;
        if(queryrest(1,1,n,l1,mid)<=x)l=mid+1,ans=mid;
        else r=mid-1;
    }
    update(1,1,n,l1,ans,1);
}
int main()
{
    n=read(),m=read();
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        int opt=read(),l=read(),r=read();
        if (opt==0) update(1,1,n,l,r,0);
        else if (opt==2) printf("%d\n",querymax(1,1,n,l,r));
        else work(l,r);
    }
    return 0;
}

 

posted @ 2020-08-04 20:30  我亦如此向往  阅读(197)  评论(0编辑  收藏  举报