【NOI 2005】【JZOJ 2413】维护数列 平衡树操作模板题

8 篇文章 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 求出当前数列中和最大的一段子列,并输出最大和

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

Analysis

本蒟蒻暂时只会打splay,这题就是用splay做的
实现细节:

  • 由于插入删除的数字可能很多,所以要开一个栈把删除的节点存起来,下次插入的时候再利用,回收空间
  • 本题中为了实现MAX-SUM操作,需对于每个点记录lx,rx,mx,分别表示从中间向左,向右能延伸的最大和,整个区间的最大和
  • 区间赋值操作的tag不能初始化为0,要为INF
  • 区间翻转操作的tag打标记的时候是^1而非=1
  • 反转操作一定要先直接交换两个子树及相关信息(本题中的左右子树相关信息为lx,rx),再打tag标记
  • 对于所有的修改操作,做完都要update(我简单粗暴将该点splay到根)

Code

#include<cstdio>
#include<stack>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=500010;
int n,tot,root,top,sta[N],size[N],f[N],a[N][2],bz[N],tag[N];
int b[N],sum[N],key[N],lx[N],rx[N],mx[N];
stack<int> q;
int pd(int x)
{
    return x==a[f[x]][1];
}
void update(int x)
{
    int ls=a[x][0],rs=a[x][1];
    size[x]=1+size[ls]+size[rs];
    sum[x]=key[x]+sum[ls]+sum[rs];
    lx[x]=max(lx[ls],sum[ls]+key[x]+lx[rs]);
    rx[x]=max(rx[rs],sum[rs]+key[x]+rx[ls]);
    mx[x]=rx[ls]+key[x]+lx[rs];
    if(ls) mx[x]=max(mx[x],mx[ls]);
    if(rs) mx[x]=max(mx[x],mx[rs]);
}
void change(int x,int z)
{
    if(!x) return;
    sum[x]=size[x]*z,key[x]=bz[x]=z;
    if(z>0) lx[x]=rx[x]=mx[x]=size[x]*z;
    else lx[x]=rx[x]=0,mx[x]=z;
}
void turn(int x)
{
    if(!x) return;
    swap(a[x][0],a[x][1]);
    swap(lx[x],rx[x]);
    tag[x]^=1;
}
void down(int x)
{
    if(!x) return;
    if(tag[x])
    {
        turn(a[x][0]),turn(a[x][1]);
        tag[x]=0;
    }
    if(bz[x]!=N)
    {
        change(a[x][0],bz[x]),change(a[x][1],bz[x]);
        bz[x]=N;
    }
}
void remove(int x,int y)
{
    for(;x!=y;x=f[x]) q.push(x);
    while(!q.empty())
    {
        down(q.top());q.pop();
    }
}
void rotate(int x)
{
    int y=f[x],z=pd(x);
    a[y][z]=a[x][1-z];
    if(a[x][1-z]) f[a[x][1-z]]=y;
    f[x]=f[y];
    if(f[y]) a[f[y]][pd(y)]=x;
    a[x][1-z]=y,f[y]=x;
    update(y);
}
void splay(int x,int y)
{
    remove(x,y);
    if(!y) root=x;
    while(f[x]!=y)
    {
        if(f[f[x]]!=y)
            if(pd(x)==pd(f[x])) rotate(f[x]);
            else rotate(x);
        rotate(x);
    }
    update(x);
}
void del(int x)
{
    if(!x) return;
    sta[++top]=x;
    del(a[x][0]);del(a[x][1]);
    f[x]=a[x][0]=a[x][1]=0;
}
int newnode(int z)
{
    int x;
    if(top) x=sta[top--];
    else x=++tot;
    size[x]=1,key[x]=sum[x]=mx[x]=z,lx[x]=rx[x]=max(z,0);
    tag[x]=0,bz[x]=N;
    return x;
}
void build(int &x,int l,int r,int fa)
{
    if(l>r) return;
    int mid=(l+r)>>1;
    x=newnode(b[mid]);
    f[x]=fa;
    if(l==r) return;
    build(a[x][0],l,mid-1,x),build(a[x][1],mid+1,r,x);
    update(x);
}
int kth(int x,int k)
{
    down(x);
    if(size[a[x][0]]+1==k) return x;
    if(k>size[a[x][0]]) return kth(a[x][1],k-size[a[x][0]]-1);
    else return kth(a[x][0],k);
}
int split(int x,int y)
{
    x=kth(root,x),y=kth(root,y+2);
    splay(x,0),splay(y,x);
    return a[y][0];
}
int main()
{
    freopen("sequence10.in","r",stdin);
    freopen("2413.out","w",stdout);
    int _,x,y,z,p,m;
    char ch;
    scanf("%d %d",&n,&_);
    fo(i,2,n+1) scanf("%d",&b[i]);
    build(root,1,n+2,0);
    scanf("\n");
    for(;_;_--)
    {
        scanf("%c",&ch);
        if(ch=='I')
        {
            scanf("NSERT %d %d",&p,&m);
            fo(i,1,m) scanf("%d",&b[i]);
            x=kth(root,p+1),y=kth(root,p+2);
            splay(x,0),splay(y,x);
            build(a[y][0],1,m,y);
            splay(a[y][0],0);
        }
        if(ch=='D')
        {
            scanf("ELETE %d %d",&x,&z);y=x+z-1;
            x=split(x,y);
            y=f[x];
            a[y][pd(x)]=0;
            del(x);
            splay(y,0);
        }
        if(ch=='M')
        {
            scanf("%c",&ch);scanf("%c",&ch);
            if(ch=='K')
            {
                scanf("E-SAME %d %d %d",&x,&y,&z);y=x+y-1;
                x=split(x,y);
                change(x,z);
                splay(x,0);
            }
            else
            {
                scanf("-SUM");
                x=kth(root,1),y=kth(root,size[root]);
                splay(x,0),splay(y,x);
                printf("%d\n",mx[a[y][0]]);
            }
        }
        if(ch=='R')
        {
            scanf("EVERSE %d %d",&x,&z);y=x+z-1;
            x=split(x,y);
            turn(x);
            splay(x,0);
        }
        if(ch=='G')
        {
            scanf("ET-SUM %d %d",&x,&z);y=x+z-1;
            x=split(x,y);
            printf("%d\n",sum[x]);
        }
        scanf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值