【NOI2005】维护数列 splay

7 篇文章 0 订阅

Description

  请写一个程序,要求维护一个数列,支持以下6种操作:(请注意,格式栏中的下划线‘ _ ’表示实际输入文件中的空格)
  1. 插入 INSERT_posi_tot_c1_c2_…_ctot 在当前数列的第posi个数字后插入tot个数字:c1, c2, …, ctot;若在数列首插入,则posi为0
  2. 删除 DELETE_posi_tot 从当前数列的第posi个数字开始连续删除tot个数字
  3. 修改 MAKE-SAME_posi_tot_c 将当前数列的第posi个数字开始的连续tot个数字统一修改为c
  4. 翻转 REVERSE_posi_tot 取出从当前数列的第posi个数字开始的tot个数字,翻转后放入原来的位置
  5. 求和 GET-SUM_posi_tot 计算从当前数列开始的第posi个数字开始的tot个数字的和并输出
  6. 求和最大的子列 MAX-SUM 求出当前数列中和最大的一段子列,并输出最大和

Input

  输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。
  第2行包含N个数字,描述初始时的数列。
  以下M行,每行一条命令,格式参见问题描述中的表格。

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

  你可以认为在任何时刻,数列中至少有1个数。
  输入数据一定是正确的,即指定位置的数在数列中一定存在。
  50%的数据中,任何时刻数列中最多含有30 000个数;
  100%的数据中,任何时刻数列中最多含有500 000个数。
  100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内。
  100%的数据中,M ≤20 000,插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。

Solution

维护一个splay,就是维护的东西有点多
因为他的insert总长度可能很长,但他同时不会太长,所以把delete的点放到一个栈里,以后加入的时候使用
需要维护以下东西(数组与我程序中的数组相同)
size[x]表示x子树的大小
da[x]表示x这个位置的真实值
sum[x]表示x子树的和(为了维护操作5)
以下为了维护操作6
l[x]表示x子树所代表的区间左边开始的最大子序列
r[x]表示x子树所代表的区间右边开始的最大子序列
d[x]表示x子树所代表的区间整体的最大子序列
如何转移自己想
注意细节

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1001000
using namespace std;
int t[N][2],fa[N],s[N],tot,root,s1[N],rev[N],lz[N],l[N],r[N],d[N],sum[N],size[N],da[N],n,m;
int lr(int x){return x==t[fa[x]][1];}
void down(int x,int v)
{
    if(!x) return;
    da[x]=lz[x]=v;
    sum[x]=v*size[x];
    if(v>0) l[x]=r[x]=d[x]=size[x]*v;
    else l[x]=r[x]=0,d[x]=v;
}
void dow1(int z)
{
    if(!z) return;
    swap(t[z][0],t[z][1]);
    swap(l[z],r[z]);
    rev[z]^=1;
}
void dow2(int z)
{
    if(!z) return;
    if(lz[z]!=N)
    {
        down(t[z][0],lz[z]);
        down(t[z][1],lz[z]);
        lz[z]=N;
    }
}
void turn(int z)
{
    if(!z) return;
    if(rev[z]) dow1(t[z][0]),dow1(t[z][1]),rev[z]=0;
}
void xc(int x,int y)
{
    s1[0]=0;
    for(;x!=y;x=fa[x]) s1[++s1[0]]=x;
    for(;s1[0];s1[0]--)
    {
        int z=s1[s1[0]];
        dow2(z);
        turn(z);
    }
}
void update(int x)
{
    if(x==1||x==n+2||x==n+3) d[x]=-214748347;
    else d[x]=r[t[x][0]]+da[x]+l[t[x][1]];
    sum[x]=sum[t[x][0]]+sum[t[x][1]]+da[x];
    size[x]=size[t[x][0]]+size[t[x][1]]+1;
    if(t[x][0]) d[x]=max(d[x],d[t[x][0]]);
    if(t[x][1]) d[x]=max(d[x],d[t[x][1]]);
    l[x]=r[x]=0;
    l[x]=max(l[t[x][0]],sum[t[x][0]]+da[x]+l[t[x][1]]);
    r[x]=max(r[t[x][1]],sum[t[x][1]]+da[x]+r[t[x][0]]);
}
void rotate(int x)
{
    int y=fa[x],k=lr(x);
    t[y][k]=t[x][1-k];if(t[x][1-k]) fa[t[x][1-k]]=y;
    fa[x]=fa[y];if(fa[y]) t[fa[y]][lr(y)]=x;
    t[x][1-k]=y;fa[y]=x;
    update(y);update(x);
}
void splay(int x,int y)
{
    xc(x,y);
    while(fa[x]!=y)
    {
        if(fa[fa[x]]!=y)
            if(lr(x)==lr(fa[x])) rotate(fa[x]);
            else rotate(x);
        rotate(x);
    }
    if(y==0) root=x;
}
int kth(int r,int x)
{
    dow2(r);
    turn(r);
    if(size[t[r][0]]+1==x) return r;
    if(size[t[r][0]]>=x) return kth(t[r][0],x);
    else return kth(t[r][1],x-size[t[r][0]]-1);
}
void del(int x)
{
    rev[x]=0;lz[x]=N;
    s[++s[0]]=x;t[fa[x]][lr(x)]=0;fa[x]=0;
    if(t[x][0]) del(t[x][0]);
    if(t[x][1]) del(t[x][1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    size[1]=1;lz[1]=N;
    fo(i,2,n+1)
    {
        scanf("%d",&da[i]);
        fa[i-1]=i;t[i][0]=i-1;lz[i]=N;
        update(i);
    }
    fa[n+1]=n+2;t[n+2][0]=n+1;lz[n+2]=N;update(n+2);
    fa[n+2]=tot=root=n+3;t[n+3][0]=n+2;lz[n+3]=N;update(n+3);
    scanf("\n");
    for(;m;m--)
    {
        char ch;
        scanf("%c",&ch);
        if(ch=='M') scanf("%c",&ch),scanf("%c",&ch);
        if(ch=='I')//----------插入 
        {
            int x,c,z;
            scanf("NSERT %d %d",&x,&c);
            int y=kth(root,x+2);x=kth(root,x+1);
            splay(x,0);splay(y,x);
            fo(i,1,c)
            {
                if(s[0]==0) s[++s[0]]=++tot;
                int z=s[s[0]--];fa[z]=y;
                if(i==1) t[y][0]=z;else t[y][1]=z;
                y=z;lz[z]=N;
                scanf("%d",&da[z]);
            }
            for(;y>0;y=fa[y]) update(y);
            scanf("\n");
        }
        if(ch=='D')//----------删除 
        {
            int x,y;scanf("ELETE %d %d\n",&x,&y);
            y=kth(root,x+y+1);x=kth(root,x);
            splay(x,0);splay(y,x);
            int z=t[y][0];
            del(z);
            update(y);update(x);
        }
        if(ch=='K')//----------修改 
        {
            int x,y,z;scanf("E-SAME %d %d %d\n",&x,&y,&z);
            y=kth(root,x+y+1);x=kth(root,x);
            splay(x,0);splay(y,x);
            down(t[y][0],z);
            splay(t[y][0],0);
        }
        if(ch=='R')//----------翻转 
        {
            int x,y;scanf("EVERSE %d %d\n",&x,&y);
            y=kth(root,x+y+1);x=kth(root,x);
            splay(x,0);splay(y,x);
            dow1(t[y][0]);
            splay(t[y][0],0);
        }
        if(ch=='G')//----------求和 
        {
            int x,y;scanf("ET-SUM %d %d\n",&x,&y);
            y=kth(root,x+y+1);x=kth(root,x);
            splay(x,0);splay(y,x);
            printf("%d\n",sum[t[y][0]]);
        }
        if(ch=='X')//----------求最大子序列 
        {
            scanf("-SUM\n");
            int x=kth(root,1),y=kth(root,size[root]);
            splay(x,0);splay(y,x);
            printf("%d\n",d[t[y][0]]);
        }
    }
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值