• BZOJ 1500/Luogu 2042


    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1500

    题目链接:https://www.luogu.org/problemnew/show/P2042

    Description

    请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格

    Input

    输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。

    第2行包含N个数字,描述初始时的数列。

    以下M行,每行一条命令,格式参见问题描述中的表格。

    任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。

    插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。

    Output

    对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

    Sample Input
    9 8
    2 -6 3 5 1 -5 -3 6 3
    GET-SUM 5 4
    MAX-SUM
    INSERT 8 3 -5 7 2
    DELETE 12 1
    MAKE-SAME 3 3 2
    REVERSE 3 6
    GET-SUM 5 4
    MAX-SUM

    Sample Output
    -1
    10
    1
    10

    HINT

    题解:

    Splay模板题。

    其中,关于如何搞定求区间最大连续子列和的问题,可以参考线段树的做法:UVALive 3938 - "Ray, Pass me the dishes!" - [最大连续子列和+线段树](通过分治+最大前缀和+最大后缀和共同维护得到最大连续子列和)(感慨一下,已经想不起是哪个时候做的这道题了,时光飞逝啊……)。

    关于区间翻转,则是Splay老生常谈的事情了,一个 $rev$ 标记搞定。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=5e5+10;
    
    int n,m;
    int a[maxn];
    
    /******************************** splay - st ********************************/
    #define Key_value ch[ch[root][1]][0]
    int root,nodecnt;
    int par[maxn],ch[maxn][2];
    int key[maxn],sum[maxn],siz[maxn];
    int mxpre[maxn],mxsuf[maxn],mxsub[maxn]; //最大前缀和,最大后缀和,最大连续子列和
    bool alt[maxn],rev[maxn]; //修改标记,反转标记
    int pool[maxn],poolsize; //节点回收
    void NewNode(int &x,int p,int k)
    {
        if(poolsize>0) x=pool[--poolsize];
        else x=++nodecnt;
        par[x]=p;
        ch[x][0]=ch[x][1]=0;
        key[x]=sum[x]=k;
        mxpre[x]=mxsuf[x]=mxsub[x]=k;
        siz[x]=1;
        alt[x]=rev[x]=0;
    }
    void Update_Rev(int x)
    {
        if(x==0) return;
        swap(ch[x][0],ch[x][1]);
        swap(mxpre[x],mxsuf[x]);
        rev[x]^=1;
    }
    void Update_Alt(int x,int val)
    {
        if(x==0) return;
        key[x]=val;
        sum[x]=siz[x]*val;
        mxpre[x]=mxsuf[x]=mxsub[x]=max(val,val*siz[x]);
        alt[x]=1;
    }
    void Pushup(int x)
    {
        int ls=ch[x][0],rs=ch[x][1];
        siz[x]=siz[ls]+siz[rs]+1;
        sum[x]=sum[ls]+sum[rs]+key[x];
        mxpre[x]=max(mxpre[ls],sum[ls]+key[x]+max(0,mxpre[rs]));
        mxsuf[x]=max(mxsuf[rs],max(0,mxsuf[ls])+key[x]+sum[rs]);
        mxsub[x]=max(max(mxsub[ls],mxsub[rs]),max(0,mxsuf[ls])+key[x]+max(0,mxpre[rs]));
    }
    void Pushdown(int x)
    {
        if(rev[x])
        {
            Update_Rev(ch[x][0]);
            Update_Rev(ch[x][1]);
            rev[x]=0;
        }
        if(alt[x])
        {
            Update_Alt(ch[x][0],key[x]);
            Update_Alt(ch[x][1],key[x]);
            alt[x]=0;
        }
    }
    void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig
    {
        int y=par[x];
        ch[y][!type]=ch[x][type]; par[ch[x][type]]=y;
        if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x;
        par[x]=par[y];
        ch[x][type]=y; par[y]=x;
        Pushup(y); Pushup(x);
    }
    void Splay(int x,int goal)
    {
        while(par[x]!=goal)
        {
            if(par[par[x]]==goal) Rotate(x,ch[par[x]][0]==x); //左孩子zig,右孩子zag
            else
            {
                int y=par[x];
                int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子
                if(ch[y][type]==x)
                {
                    Rotate(x,!type);
                    Rotate(x,type);
                }
                else
                {
                    Rotate(y,type);
                    Rotate(x,type);
                }
            }
        }
        if(goal==0) root=x;
    }
    int Get_Kth(int x,int k) //得到第k个节点
    {
        Pushdown(x);
        int t=siz[ch[x][0]]+1;
        if(t==k) return x;
        if(t>k) return Get_Kth(ch[x][0],k);
        else return Get_Kth(ch[x][1],k-t);
    }
    void Build(int &x,int l,int r,int par) //建树,先建立中间结点,再建两端的方法
    {
        if(l>r) return;
        int mid=(l+r)/2;
        NewNode(x,par,a[mid]);
        Build(ch[x][0],l,mid-1,x);
        Build(ch[x][1],mid+1,r,x);
        Pushup(x);
    }
    void Init() //初始化,前后各加一个空节点
    {
        root=nodecnt=poolsize=0;
        par[0]=ch[0][0]=ch[0][1]=0;
        key[0]=sum[0]=siz[0]=0;
        alt[0]=rev[0]=0;
        mxpre[0]=mxsuf[0]=mxsub[0]=-INF;
        NewNode(root,0,-INF); //头部加入一个空位
        NewNode(ch[root][1],root,-INF); //尾部加入一个空位
        Build(Key_value,1,n,ch[root][1]);
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    void Insert(int p,int tot)
    {
        for(int i=1;i<=tot;i++) scanf("%d",&a[i]);
        Splay(Get_Kth(root,p+0+1),0); //p伸展到根
        Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子
        Build(Key_value,1,tot,ch[root][1]);
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    void Collect(int x) //回收节点x统领的子树
    {
        if(x==0) return;
        pool[poolsize++]=x;
        Collect(ch[x][0]);
        Collect(ch[x][1]);
    }
    void Delete(int p,int tot)
    {
        Splay(Get_Kth(root,p-1+1),0); //伸展到根
        Splay(Get_Kth(root,p+tot+1),root); //伸展到根的右孩子
        Collect(Key_value);
        par[Key_value]=0;
        Key_value=0;
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    void Alter(int p,int tot,int c) //修改[p,p+tot)为k
    {
        Splay(Get_Kth(root,p-1+1),0); //伸展到根
        Splay(Get_Kth(root,p+tot+1),root); //伸展到根的右孩子
        Update_Alt(Key_value,c);
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    void Reverse(int p,int tot) //反转[p,p+tot)区间
    {
        Splay(Get_Kth(root,p-1+1),0);
        Splay(Get_Kth(root,p+tot+1),root);
        Update_Rev(Key_value);
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    int Get_Sum(int p,int tot)
    {
        Splay(Get_Kth(root,p-1+1),0);
        Splay(Get_Kth(root,p+tot+1),root);
        return sum[Key_value];
    }
    
    int Get_MaxSub(int p,int tot)
    {
        Splay(Get_Kth(root,p-1+1),0);
        Splay(Get_Kth(root,p+tot+1),root);
        return mxsub[Key_value];
    }
    /******************************** splay - ed ********************************/
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        Init();
    
        char op[12];
        int pos,tot,c;
        while(m--)
        {
            scanf("%s",op);
            switch(op[0]&op[1]|op[2])
            {
            case ('I'&'N'|'S'):
                scanf("%d%d",&pos,&tot);
                Insert(pos,tot);
                break;
            case ('D'&'E'|'L'):
                scanf("%d%d",&pos,&tot);
                Delete(pos,tot);
                break;
            case ('M'&'A'|'K'):
                scanf("%d%d%d",&pos,&tot,&c);
                Alter(pos,tot,c);
                break;
            case ('R'&'E'|'V'):
                scanf("%d%d",&pos,&tot);
                Reverse(pos,tot);
                break;
            case ('G'&'E'|'T'):
                scanf("%d%d",&pos,&tot);
                printf("%d
    ",Get_Sum(pos,tot));
                break;
            case ('M'&'A'|'X'):
                printf("%d
    ",Get_MaxSub(1,siz[root]-2));
                break;
            }
        }
    }
  • 相关阅读:
    Android 侧滑(双向滑动菜单)效果
    Android中PopupWindow中有输入框时无法弹出输入法的解决办法
    Android 调用图库选择图片实现和参数详解
    5.抽象类篇
    4.事件篇
    3.委托篇
    2.结构篇
    1.枚举篇
    读取excel到数据库里面
    Windows系统安装docker
  • 原文地址:https://www.cnblogs.com/dilthey/p/9823461.html
Copyright © 2020-2023  润新知