BZOJ 1500/Luogu 2042 - 维修数列 - [NOI2005][Splay]

题目链接: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\n",Get_Sum(pos,tot));
            break;
        case ('M'&'A'|'X'):
            printf("%d\n",Get_MaxSub(1,siz[root]-2));
            break;
        }
    }
}

 

posted @ 2018-10-20 23:37  Dilthey  阅读(192)  评论(0编辑  收藏  举报