[NOI2005]维修数列

Description

Input

输入的第11行包含两个数NNM(M20000)M(M≤20000)NN表示初始时数列中数的个数,MM表示要进行的操作数目。
22行包含NN个数字,描述初始时的数列。
以下MM行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500000500000个数,数列中任何一个数字均在[1000,1000][−1000,1000]内。
插入的数字总数不超过40000004000000个,输入文件大小不超过20MBytes20MBytes

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

Source

思路

有几个要注意的地方:
1. MAX-SUM操作的返回值可以为负数!
2. 如果数组大小开4×1064×106是会炸空间的,只能开到5×1055×105,需要加一个垃圾回收函数(就是将删除的树的节点放入栈中,insert时使用栈中的节点)。
3. 求MAX-SUM的值可以参见这一篇博客:最大连续子数列和(by Wolfycz)
然后就是用BST维护了。

代码

(代码略长,常数略大,勿喷)
数组意义:
fafasonsonvalvalsizesize应该不需要讲了吧;
icovicov存是否被覆盖,如果为11,那么covcov存覆盖的值;
revrev存是否被反转;
slslsrsr表示从一个点的子树中左边(或右边)能延伸的最大和;
ff表示子树中最大连续子序列和。

#include <cstdio>

const int maxn=500000;
const int inf=0x3f3f3f3f;

struct stack//方便回收空间(其实也可以用队列)
{
  int s[maxn+10],head;

  inline int push(int x)
  {
    ++head;
    s[head]=x;
    return 0;
  }

  inline int top()
  {
    return s[head];
  }

  inline int pop()
  {
    --head;
    return 0;
  }

  inline int mem()//初始化,使取出的节点开始按照1,2,3...的顺序
  {
    for(register int i=maxn; i; --i)
      {
        s[maxn-i+1]=i;
      }
    head=maxn;
    return 0;
  }
};

int a[maxn+10];
stack sta;

inline int max(int a,int b)
{
  return a>b?a:b;
}

inline int swap(int &a,int &b)
{
  int t=a;
  a=b;
  b=t;
  return 0;
}

struct splay_tree
{
  int fa[maxn+10],son[2][maxn+10],rev[maxn+10],cov[maxn+10];
  int val[maxn+10],sum[maxn+10],size[maxn+10],root,icov[maxn+10];
  int sl[maxn+10],sr[maxn+10],f[maxn+10];

  inline int t(int x)
  {
    return son[1][fa[x]]==x;
  }

  inline int uf(int x)//更新sl,sr和f(这个有点麻烦,自己想想吧)
  {
    sl[x]=max(son[0][x]?sl[son[0][x]]:-inf,sum[son[0][x]]+val[x]+max(0,sl[son[1][x]]));
    sr[x]=max(son[1][x]?sr[son[1][x]]:-inf,sum[son[1][x]]+val[x]+max(0,sr[son[0][x]]));
    f[x]=max(max(son[0][x]?f[son[0][x]]:-inf,son[1][x]?f[son[1][x]]:-inf),max(sr[son[0][x]],0)+val[x]+max(sl[son[1][x]],0));
    return 0;
  }

  inline int updata(int x)//更新size,sum,sl,sr和f
  {
    size[x]=size[son[0][x]]+size[son[1][x]]+1;
    sum[x]=val[x]+sum[son[0][x]]+sum[son[1][x]];
    uf(x);
    return 0;
  }

  inline int push_rev(int x)//将一个点打上rev标记,同时交换左右儿子
  {
    rev[x]^=1;
    swap(son[0][x],son[1][x]);
    swap(sl[x],sr[x]);//左边和右边的最大和需要互换
    return 0;
  }

  inline int push_cover(int x,int v)//将一个点打上cov标记,同时改变val等值
  {
    icov[x]=1;
    cov[x]=v;
    val[x]=v;
    sum[x]=size[x]*v;
    sl[x]=sr[x]=f[x]=max(v,size[x]*v);
    return 0;
  }

  inline int pushdown(int x)//下传标记
  {
    if(icov[x])
      {
        if(son[0][x])
          {
            push_cover(son[0][x],cov[x]);
          }
        if(son[1][x])
          {
            push_cover(son[1][x],cov[x]);
          }
        icov[x]=0;
      }
    if(rev[x])
      {
        if(son[0][x])
          {
            push_rev(son[0][x]);
          }
        if(son[1][x])
          {
            push_rev(son[1][x]);
          }
        rev[x]=0;
      }
    return 0;
  }

  inline int rotate(int x)//zig或zag
  {
    int k=t(x),f=fa[x];
    if(fa[f])
      {
        son[t(f)][fa[f]]=x;
      }
    fa[x]=fa[f];
    if(son[!k][x])
      {
        fa[son[!k][x]]=f;
      }
    son[k][f]=son[!k][x];
    fa[f]=x;
    son[!k][x]=f;
    updata(f);
    updata(x);
    return 0;
  }

  inline int splay(int x,int c)//旋转x到c的儿子
  {
    while(fa[x]!=c)
      {
        int f=fa[x];
        if(fa[f]==c)
          {
            rotate(x);
          }
        else if(t(f)==t(x))
          {
            rotate(f);
            rotate(x);
          }
        else
          {
            rotate(x);
            rotate(x);
          }
      }
    if(!c)
      {
        root=x;
      }
    return 0;
  }

  int reuse(int x)//回收以x为根的子树,释放空间
  {
    if(son[0][x])
      {
        reuse(son[0][x]);
      }
    if(son[1][x])
      {
        reuse(son[1][x]);
      }
    sta.push(x);
    return 0;
  }

  inline int getkth(int k)//寻找第k大
  {
    int now=root;
    while(now)
      {
        pushdown(now);//这里用到了pushdown,接下来就只需要调用getkth完成pushdown
        if(size[son[0][now]]+1==k)
          {
            return now;
          }
        else if(size[son[0][now]]+1<k)
          {
            k-=size[son[0][now]]+1;
            now=son[1][now];
          }
        else
          {
            now=son[0][now];
          }
      }
    return 0;
  }

  inline int insert(int l,int r)//将a数组中l到r的区间建立成一棵树,并返回这棵树的根
  {
    int x=sta.top(),mid=(l+r)>>1;
    sta.pop();
    val[x]=a[mid];
    icov[x]=rev[x]=0;
    sl[x]=sr[x]=f[x]=max(0,a[mid]);
    size[x]=r-l+1;
    if(l<=mid-1)
      {
        son[0][x]=insert(l,mid-1);
        fa[son[0][x]]=x;
      }
    else
      {
        son[0][x]=0;
      }
    if(mid+1<=r)
      {
        son[1][x]=insert(mid+1,r);
        fa[son[1][x]]=x;
      }
    else
      {
        son[1][x]=0;
      }
    updata(x);
    return x;
  }

  inline int ins(int pos,int tot)//在pos后插入a[1-tot],注意直接建成一条链会TLE,必须二分建树
  {
    if(!root)
      {
        root=insert(1,tot);
        fa[root]=0;
        return 0;
      }
    else if(!pos)
      {
        int now=getkth(1);
        splay(now,0);
        son[0][now]=insert(1,tot);
        fa[son[0][now]]=now;
        updata(root);
        return 0;
      }
    else if(pos==size[root])
      {
        int now=getkth(size[root]);
        splay(now,0);
        son[1][now]=insert(1,tot);
        fa[son[1][now]]=now;
        updata(root);
        return 0;
      }
    else
      {
        int x=getkth(pos);
        splay(x,0);
        int y=getkth(pos+1);
        splay(y,x);
        son[0][y]=insert(1,tot);
        fa[son[0][y]]=y;
        updata(y);
        updata(x);
        return 0;
      }
  }

  inline int del(int l,int r)//删去l-r节点
  {
    if(l==1)
      {
        if(r==size[root])
          {
            reuse(root);
            root=0;
          }
        else
          {
            int x=getkth(r+1);
            splay(x,0);
            reuse(son[0][x]);
            son[0][x]=0;
            updata(x);
          }
      }
    else
      {
        int x=getkth(l-1);
        splay(x,0);
        if(r==size[root])
          {
            reuse(son[1][x]);
            son[1][x]=0;
          }
        else
          {
            int y=getkth(r+1);
            splay(y,x);
            reuse(son[0][y]);
            son[0][y]=0;
            updata(y);
          }
        updata(x);
      }
    return 0;
  }

  inline int reverse(int l,int r)//反转l-r节点
  {
    if(l==1)
      {
        if(r==size[root])
          {
            push_rev(root);
          }
        else
          {
            int x=getkth(r+1);
            splay(x,0);
            push_rev(son[0][x]);
            uf(x);
          }
      }
    else
      {
        int x=getkth(l-1);
        splay(x,0);
        if(r==size[root])
          {
            push_rev(son[1][x]);
          }
        else
          {
            int y=getkth(r+1);
            splay(y,x);
            push_rev(son[0][y]);
            uf(y);
          }
        uf(x);
      }
    return 0;
  }

  inline int getsum(int l,int r)//返回l-r的连加和
  {
    if(l==1)
      {
        if(r==size[root])
          {
            return sum[root];
          }
        else
          {
            int x=getkth(r+1);
            splay(x,0);
            return sum[son[0][x]];
          }
      }
    else
      {
        int x=getkth(l-1);
        splay(x,0);
        if(r==size[root])
          {
            return sum[son[1][x]];
          }
        else
          {
            int y=getkth(r+1);
            splay(y,x);
            return sum[son[0][y]];
          }
      }
  }

  inline int cover(int l,int r,int v)//将l-r覆盖为v
  {
    if(l==1)
      {
        if(r==size[root])
          {
            push_cover(root,v);
          }
        else
          {
            int x=getkth(r+1);
            splay(x,0);
            push_cover(son[0][x],v);
            updata(x);
          }
      }
    else
      {
        int x=getkth(l-1);
        splay(x,0);
        if(r==size[root])
          {
            push_cover(son[1][x],v);
          }
        else
          {
            int y=getkth(r+1);
            splay(y,x);
            push_cover(son[0][y],v);
            updata(y);
          }
        updata(x);
      }
    return 0;
  }

  inline int getmax()//返回最大连续子序列和
  {
    return f[root];
  }
};

splay_tree st;
int n,m,x,y,z;
char s[10];

int main()
{
  sta.mem();
  scanf("%d%d",&n,&m);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&a[i]);
    }
  st.ins(0,n);//这里相当于省去了开始的build步骤
  while(m--)
    {
      scanf("%s",s);
      if(s[0]=='I')
        {
          scanf("%d%d",&x,&y);
          for(register int i=1; i<=y; ++i)
            {
              scanf("%d",&a[i]);
            }
          st.ins(x,y);
        }
      else if(s[0]=='D')
        {
          scanf("%d%d",&x,&y);
          st.del(x,x+y-1);
        }
      else if(s[0]=='M')
        {
          if(s[2]=='K')
            {
              scanf("%d%d%d",&x,&y,&z);
              st.cover(x,x+y-1,z);
            }
          else
            {
              printf("%d\n",st.getmax());
            }
        }
      else if(s[0]=='R')
        {
          scanf("%d%d",&x,&y);
          st.reverse(x,x+y-1);
        }
      else
        {
          scanf("%d%d",&x,&y);
          printf("%d\n",st.getsum(x,x+y-1));
        }
    }
  return 0;
}

(代码好难打啊……)

转载于:https://www.cnblogs.com/Canopus-wym/p/10376255.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值