SHOI2015 脑洞治疗仪

题目链接:戳我

luogu的题面真心好看!!!qwqwq

按照专业的话来讲,这个题是——线段树维护最大连续子段

好吧。。其实就和前两天写的那个区间最大字段和很相似嘛,就是左右线段树可以合并这种维护信息类型的题目。

操作0是线段树区间赋值

操作1是线段树区间赋值+线段树区间前K个赋值(前K个怎么实现呢?就是我们在线段树上直接二分,然后找到那些0的个数小于等于K的区间们,然后一个一个依次赋值)

操作2是求线段树中最大相同子段长度

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 200010
using namespace std;
int n,m;
int le[MAXN<<2],ri[MAXN<<2];
struct Node{int maxl,maxr,maxs,sum0,sum1,tag;}t[MAXN<<2];
 
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
 
inline void update(int x,int k)
{
    if(k==0)
    {
        t[x].sum0=t[x].maxl=t[x].maxr=t[x].maxs=ri[x]-le[x]+1;
        t[x].sum1=0;
    }
    else if(k==1)
    {
        t[x].sum1=ri[x]-le[x]+1;
        t[x].sum0=t[x].maxl=t[x].maxr=t[x].maxs=0;
    }
    t[x].tag=k;
}
 
inline void push_up(int x)
{
    t[x].sum0=t[ls(x)].sum0+t[rs(x)].sum0;
    t[x].sum1=t[ls(x)].sum1+t[rs(x)].sum1;
    t[x].maxl=t[ls(x)].maxl;
    if(t[ls(x)].maxl==ri[ls(x)]-le[ls(x)]+1) t[x].maxl=max(t[x].maxl,t[ls(x)].maxl+t[rs(x)].maxl);
    t[x].maxr=t[rs(x)].maxr;
    if(t[rs(x)].maxr==ri[rs(x)]-le[rs(x)]+1) t[x].maxr=max(t[x].maxr,t[rs(x)].maxr+t[ls(x)].maxr);
    t[x].maxs=max(max(t[ls(x)].maxs,t[rs(x)].maxs),t[ls(x)].maxr+t[rs(x)].maxl);
}
 
inline void push_down(int x)
{
    if(t[x].tag!=-1)
    {
        update(ls(x),t[x].tag);
        update(rs(x),t[x].tag);
        t[x].tag=-1;
    }
}
 
inline void build(int x,int l,int r)
{
    t[x].tag=-1;
    le[x]=l; ri[x]=r;
    if(le[x]==ri[x]){t[x].sum1=1;return;}
    int mid=(le[x]+ri[x])>>1;
    build(ls(x),le[x],mid);
    build(rs(x),mid+1,ri[x]);
    push_up(x);
}
 
inline void clean(int x,int ll,int rr)
{
    if(ll==le[x]&&ri[x]==rr) {update(x,0);return;}
    int mid=(le[x]+ri[x])>>1;
    push_down(x);
    if(rr<=mid) clean(ls(x),ll,rr);
    else if(mid<ll) clean(rs(x),ll,rr);
    else clean(ls(x),ll,mid),clean(rs(x),mid+1,rr);
    push_up(x);
}
 
inline int que_sum(int x,int ll,int rr)
{
    if(ll==le[x]&&ri[x]==rr) return t[x].sum1;
    int mid=(le[x]+ri[x])>>1;
    push_down(x);
    if(rr<=mid) return que_sum(ls(x),ll,rr);
    else if(mid<ll) return que_sum(rs(x),ll,rr);
    else return que_sum(ls(x),ll,mid)+que_sum(rs(x),mid+1,rr);
}
 
inline int query(int x,int ll,int rr)
{
    if(ll==le[x]&&ri[x]==rr) return t[x].maxs;
    int mid=(le[x]+ri[x])>>1;
    push_down(x);
    if(rr<=mid) return query(ls(x),ll,rr);
    else if(mid<ll) return query(rs(x),ll,rr);
    else
    {
        int cur1=query(ls(x),ll,mid);
        int cur2=query(rs(x),mid+1,rr);
        int z1=min(t[ls(x)].maxr,mid-ll+1);
        int z2=min(t[rs(x)].maxl,rr-mid);
        return max(max(cur1,cur2),z1+z2);
    }
}
 
inline void mend(int x,int ll,int rr,int &k)
{
    if(k<=0) return;
    if(ll==le[x]&&ri[x]==rr&&k>=t[x].sum0)
    {
        k-=t[x].sum0;
        update(x,1);
        return;
    }
    int mid=(le[x]+ri[x])>>1;
    push_down(x);
    if(rr<=mid) mend(ls(x),ll,rr,k);
    else if(mid<ll) mend(rs(x),ll,rr,k);
    else
    {
        mend(ls(x),ll,mid,k);
        if(k>=0) mend(rs(x),mid+1,rr,k);
        else k=0;
    }
    push_up(x);
}
 
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op,l,r,ll,rr;
        scanf("%d",&op);
        if(op==0)
        {
            scanf("%d%d",&l,&r);
            clean(1,l,r);
        }
        else if(op==1)
        {
            scanf("%d%d%d%d",&l,&r,&ll,&rr);
            int k=que_sum(1,l,r);
            clean(1,l,r);
            mend(1,ll,rr,k);
        }
        else   
        {
            scanf("%d%d",&l,&r);
            printf("%d\n",query(1,l,r));
        }
    }
    return 0;
}

不过我写的原先不是把l,r写在struct里面的那种写法,是直接跟在函数里面的。但是之后发现。。。。不知道为什么它的左右区间不对应。。。之后就重构了一遍写了这种写法。。。。菜是原罪。。整天都是莫名其妙的锅qwqwq

posted @ 2019-02-22 15:19  风浔凌  阅读(148)  评论(0编辑  收藏  举报