[NOI 2005]维护数列

[NOI 2005]维护数列

题意:
题意
题解:
平衡树区间操作大礼包
以下是 fhqTreap 的
关于 fhq 的原理可以看(理解 split 和 merge 时自己多画点图)这个
fhq的区间操作很简单,和线段树是一样的,都是维护一些\(tag\)
如果你要对\([l,r]\)区间加
你就先\(split(root,l,x,y)\),再\(split(y,r-l+1,y,z)\)
再对\(y\)打一个\(tag_sum\),标记下传即可
回到这题
最大子段和用分治做,维护\(lmax,rmax,mmax\)表示从最左端起的最大子段和,最右端起的,整段的
转移看代码,很容易理解
复杂度:\(O(n\log n)\)
听说splay常数比较小,可是splay太长不想打
其实是我不会打

#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
inline void read(int& x)
{
    x=0;char c=getchar();int f=1;
    while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
    x*=f;
}
int sta[maxn];
queue<int> rubbish;//垃圾回收
namespace fhqTreap
{
#define ls son[rt][0]
#define rs son[rt][1]
#define inf 0x3f3f3f3f
    int son[maxn][2],siz[maxn],val[maxn],rnd[maxn],sum[maxn];//sum是区间和
    int lmax[maxn],rmax[maxn],mmax[maxn],cov[maxn];//cov是区间覆盖的tag
    bool lazy_reverse[maxn];//是否翻转tag
    int root,tot,x,y,z;
    void del(const int& rt)//删除掉这颗子树
    {
        if(ls) del(ls);
        rubbish.push(rt);
        if(rs) del(rs);
    }
    inline int newnode(int v)//新建节点
    {
        int rt;
        if(rubbish.empty()) rt=++tot;
        else rt=rubbish.front(),rubbish.pop();//如果垃圾桶里还有就用
        siz[rt]=1;rnd[rt]=rand();
        val[rt]=sum[rt]=lmax[rt]=mmax[rt]=rmax[rt]=v;
        ls=rs=0;
        cov[rt]=inf,lazy_reverse[rt]=0;
        return rt;
    }
    inline void cover_reverse(int rt,int flag,int v)
    //覆盖优先级大于翻转(因为你覆盖完了就没有翻转的必要了)
    {
        if(v!=inf)//cover
        {
            val[rt]=v,sum[rt]=v*siz[rt];
            lmax[rt]=mmax[rt]=rmax[rt]=(v>0?sum[rt]:val[rt]);
            cov[rt]=v;
            flag=0;
        }
        if(flag)//reverse
        {
            lazy_reverse[rt]^=1;
            swap(lmax[rt],rmax[rt]);
        }
    }
    inline void pushup(int& rt)//上传更新父节点数据
    {
        if(!rt) return;
        siz[rt]=1;
        mmax[rt]=lmax[rt]=rmax[rt]=sum[rt]=val[rt];
        if(ls)
        {
            mmax[rt]=max(max(mmax[rt],mmax[ls]),rmax[ls]+lmax[rt]);//子段和转移
            lmax[rt]=max(lmax[ls],sum[ls]+lmax[rt]);
            rmax[rt]=max(rmax[rt],rmax[ls]+sum[rt]);
            sum[rt]+=sum[ls],siz[rt]+=siz[ls];
        }
        if(rs)
        {
            mmax[rt]=max(max(mmax[rt],mmax[rs]),lmax[rs]+rmax[rt]);
            rmax[rt]=max(rmax[rs],sum[rs]+rmax[rt]);
            lmax[rt]=max(lmax[rt],sum[rt]+lmax[rs]);
            sum[rt]+=sum[rs],siz[rt]+=siz[rs];
        }
    }
    inline void pushdown(int rt)//标记下传
    {
#define rev lazy_reverse[rt]
        if((!rev&&cov[rt]==inf)||!rt) return;
        if(ls) cover_reverse(ls,rev,cov[rt]);
        if(rs) cover_reverse(rs,rev,cov[rt]);
        if(rev) swap(ls,rs);
        rev=0,cov[rt]=inf;
#undef rev
    }
    void split(int rt,int k,int& x,int& y)//注意区间问题必须用rank分裂
    {
        if(!rt) x=y=0;
        else
        {
            pushdown(rt);
            if(k>siz[ls]) x=rt,split(rs,k-siz[ls]-1,rs,y);
            else y=rt,split(ls,k,x,ls);
            pushup(rt);
        }
    }
    int merge(int x,int y)//注意如果你把0给上传或者下传了会发生一些奇怪的错误(我是RE)
    {
        if(!x&&!y) return 0;
        if(!x) {pushdown(y);return y;}
        if(!y) {pushdown(x);return x;}
        pushdown(x),pushdown(y);
        if(rnd[x]<rnd[y])
        {
            son[x][1]=merge(son[x][1],y);
            pushup(x);
            return x;
        }
        else
        {
            son[y][0]=merge(x,son[y][0]);
            pushup(y);
            return y;
        }
    }
    inline int build(int a[],const int& l,const int& r)//笛卡尔树O(n)建树的方法
    {
        int top=0;
        for(int i=l;i<=r;++i) a[i]=newnode(a[i]);
        for(int i=l;i<=r;++i)
        {
            while(top&&rnd[a[i]]<rnd[sta[top]]) 
                pushup(sta[top]),son[a[i]][0]=sta[top--];
            if(top) son[sta[top]][1]=a[i];
            sta[++top]=a[i];
        }
        while(top) pushup(sta[top--]);
        return sta[1];
    }
    inline void insert(int a[],int pos,int len)
    //连续插入一段数,先建好在直接merge进去。直接一个个插会TLE
    {
        y=build(a,1,len);
        split(root,pos,x,z);
        root=merge(merge(x,y),z);
    }
    inline void del(int pos,int len)//删除区间
    {
        split(root,pos-1,x,y);
        split(y,len,y,z);
        del(y);
        root=merge(x,z);
    }
    inline void cover(int pos,int len,int v)//区间覆盖
    {
        split(root,pos-1,x,y);
        split(y,len,y,z);
        cover_reverse(y,0,v);
        root=merge(merge(x,y),z);
    }
    inline void reverse(int pos,int len)//区间翻转
    {
        split(root,pos-1,x,y);
        split(y,len,y,z);
        cover_reverse(y,1,inf);
        root=merge(merge(x,y),z);
    }
    inline int getsum(int pos,int len)//区间和
    {
        split(root,pos-1,x,y);
        split(y,len,y,z);
        int ans=sum[y];
        root=merge(merge(x,y),z);
        return ans;
    }
    inline int getmaxsum()//最大子段和
    {
        return mmax[root];
    }
    
    void print(int rt)//输出整棵树
    {
        if(ls) print(ls);
        printf("%d ",val[rt]);
        if(rs) print(rs);
    }
}

using namespace fhqTreap;
int a[maxn];
int main()
{
    //freopen("test.in","r",stdin);
    srand(time(NULL));
    int n,m,len,pos;
    read(n),read(m);
    for(int i=1;i<=n;++i) read(a[i]);
    root=build(a,1,n);
    //print(root);puts(" ");
    char op[10];
    while(m--)
    {
        scanf("%s",op);
        if(op[0]=='M'&&op[5]=='U')
        {
            printf("%d\n",getmaxsum());
            continue;
        }
        read(pos),read(len);
        if(op[0]=='I')
        {
            for(int i=1;i<=len;++i) read(a[i]);
            insert(a,pos,len);
        }
        else if(op[0]=='D') del(pos,len);
        else if(op[0]=='M') read(a[1]),cover(pos,len,a[1]);
        else if(op[0]=='R') reverse(pos,len);
        else if(op[0]=='G') printf("%d\n",getsum(pos,len));
        //print(root);puts(" ");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/123789456ye/p/12193179.html
今日推荐