BZOJ 1500 [NOI2005]维修数列 (Splay)

Description

Input
输入的第 1 1 行包含两个数N M M (M2104), N N 表示初始时数列中数的个数,M表示要进行的操作数目.
2 2 行包含N个数字,描述初始时的数列.
以下 M M 行,每行一条命令,格式参见问题描述中的表格.
任何时刻数列中最多含有5105个数,数列中任何一个数字均在 [103,103] [ − 10 3 , 10 3 ] 内.
插入的数字总数不超过 4106 4 ∗ 10 6 个,输入文件大小不超过 20MB 20 M B

Output
对于输入数据中的 GETSUM G E T − S U M MAXSUM M A X − S U M 操作,向输出文件依次打印结果,每个答案(数字)占一行.

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

Data Size & Hint
你可以认为在任何时刻,数列中至少有 1 1 个数
输入数据一定是正确的,即指定位置的数在数列中一定存在
50%的数据中,任何时刻数列中最多含有 3104 3 ∗ 10 4 个数
100% 100 % 的数据中,任何时刻数列中最多含有 5105 5 ∗ 10 5 个数
100% 100 % 的数据中,任何时刻数列中任何一个数字均在 [103,103] [ − 10 3 , 10 3 ]
100% 100 % 的数据中, M2104 M ≤ 2 ∗ 10 4 ,插入的数字总数不超过 4106 4 ∗ 10 6

Solution

这个题目的操作很多,也很杂,在做题时推荐先想好操作之间的关系,然后一个一个去解决

预备工作

Q1 如何建树?

考虑到后面需要区间插入,所以直接区间离线建树,再将根节点插入原序列中
并且建树时插入前后两个哨兵节点,方便后面提取区间进行操作

Q2 需要维护些什么?

显然的,翻转标记 (rev) ( r e v ) 和区间修改标记 (tag) ( t a g ) 是必须要有的,第6个操作暂时先不考虑

1. INSERT

基操 1 ∗ 1
由于是将一段数插入到某点后方
先将其建成一棵小平衡树
然后将插入位置提取出来
将根节点作为插入点的右儿子即可

2. DELETE

基操 2 ∗ 2
直接将区间提取出来后将其与父亲节点联系断开即可
由于总插入数量可能达到 4106 4 ∗ 10 6 ,所以将删除的点编号回收 (recycle) ( r e c y c l e ) 以供下次插入时使用

3. MAKE-SAME

基操 3 ∗ 3
将区间提取出来,打上标记,解决

4. REVERSE

基操 4 ∗ 4
将区间提取出来,打上标记,解决

5. GET-SUM

基操 5 ∗ 5
考虑多维护一个数组 sum s u m ,表示其当前节点为根节点构成的子树的总和
这个标记会与前面的 INSERT,DELETE,MAKESAME I N S E R T , D E L E T E , M A K E − S A M E 进行联动,在操作时记得维护就好

6. MAX-SUM

非基操,独秀同学你可以上来了
一般来说,维护一个数列的最值可以通过枚举区间的方式
Splay S p l a y 的性质决定了他非常的适合使用这样的方式
每棵子树代表的区间,维护区间
1. 左端点开始的最大子序列 lx l x
2. 右端点开始的最大子序列 rx r x
3. 区间最大子序列 mx m x

很容易的就能写 update u p d a t e 的方程

lx[x]=max(lx[L],sum[L]+key[x]+lx[R] l x [ x ] = m a x ( l x [ L ] , s u m [ L ] + k e y [ x ] + l x [ R ]

rx[x]=max(rx[R],sum[R]+key[x]+rx[L] r x [ x ] = m a x ( r x [ R ] , s u m [ R ] + k e y [ x ] + r x [ L ]

mx[x]=max(mx[L],mx[R],rx[L]+key[x]+rx[R] m x [ x ] = m a x ( m x [ L ] , m x [ R ] , r x [ L ] + k e y [ x ] + r x [ R ]

通过枚举区间选不选 x x ,统计出答案,然后一路合并上去,最终mx[rt]就是总区间最大子序列

这个标记会与前面的 INSERT,DELETE,MAKESAME,REVERSE I N S E R T , D E L E T E , M A K E − S A M E , R E V E R S E 进行联动
INSERT I N S E R T DELETE D E L E T E 比较简单,不表
MAKESAME M A K E − S A M E
若当前节点小于 0 0 ,那么其lx[x]=rx[x]=0(我选择不选) mx[x]=key[x] m x [ x ] = k e y [ x ] (只选一个使其尽量大)
若当前节点大于等于 0 0 ,那么其lx[x]=rx[x]=mx[x]=key[x]siz[x]

REVERSE R E V E R S E
直接调换 lx[x],rx[x] l x [ x ] , r x [ x ] 即可

这个题目难度不是很大,但是码量惊人(对本蒟来说)
能让人调到心态崩溃
所以,当你实在过不了的时候






重写吧!

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define L ch[x][0]
#define R ch[x][1]
#define mid ((l+r)>>1)
const int N = 500010;
const int INF = 2100000000;
int a[N];
queue <int> q;
struct SPLAY {
    int f[N],ch[N][2],key[N],siz[N],rt,cnt;
    int lx[N],rx[N],mx[N],sum[N];
    bool tag[N],rev[N];
    void Rev(int x) {swap(L,R);swap(lx[x],rx[x]);}
    void update(int x) {
        sum[x]=sum[L]+sum[R]+key[x];
        siz[x]=1+siz[L]+siz[R];
        mx[x]=max(mx[L],max(mx[R],rx[L]+key[x]+lx[R]));
        lx[x]=max(lx[L],sum[L]+key[x]+lx[R]);
        rx[x]=max(rx[R],sum[R]+key[x]+rx[L]);
    }
    void pushdown(int x) {
        if(tag[x]) {
            rev[x]=tag[x]=0;
            if(L) {tag[L]=1;key[L]=key[x];sum[L]=siz[L]*key[x];}
            if(R) {tag[R]=1;key[R]=key[x];sum[R]=siz[R]*key[x];}
            if(key[x]>=0) {
                if(L) lx[L]=rx[L]=mx[L]=sum[L];
                if(R) lx[R]=rx[R]=mx[R]=sum[R];
            }
            else {
                if(L) {lx[L]=rx[L]=0;mx[L]=key[L];}
                if(R) {lx[R]=rx[R]=0;mx[R]=key[R];}
            }
        }
        if(rev[x]) {
            rev[x]=0;rev[L]^=1;rev[R]^=1;
            Rev(L);Rev(R);
        }
    }
    bool get(int x) {return ch[f[x]][1]==x;}
    void rotate(int x) {
        int old=f[x],oldf=f[old],w=get(x);
        pushdown(old);pushdown(x);
        ch[old][w]=ch[x][w^1];ch[x][w^1]=old;
        f[old]=x;f[ch[old][w]]=old;f[x]=oldf;
        if(oldf) ch[oldf][ch[oldf][1]==old]=x;
        update(old);update(x);
    }
    void splay(int x,int tar) {
        for(int fa;(fa=f[x])!=tar;rotate(x))
            if(f[fa]!=tar)
                rotate(get(x)==get(fa)?fa:x);
        if(!tar) rt=x;
    }
    int findbyrank(int rank) {
        int x=rt;
        while(1) {
            pushdown(x);
            if(siz[L]>=rank) {x=L;}
            else {
                rank-=siz[L]+1;
                if(!rank) return x;
                x=R;
            }
        }
    }
    void clear(int x) {L=R=f[x]=lx[x]=rx[x]=mx[x]=tag[x]=rev[x]=key[x]=siz[x]=sum[x]=0;}
    void recycle(int x) {
        if(L) recycle(L);
        if(R) recycle(R);
        q.push(x);
        clear(x);
    }
    int build(int l,int r,int fa) {
        if(l>r) return 0;
        int x;
        if(!q.empty()) {x=q.front();q.pop();}
        else {x=++cnt;}
        f[x]=fa;key[x]=a[mid];siz[x]=1;sum[x]=key[x];
        lx[x]=rx[x]=max(key[x],0);mx[x]=key[x];

        if(l<mid) L=build(l,mid-1,x);
        if(r>mid) R=build(mid+1,r,x);
        update(x);
        return x;
    }
    void insert(int pos,int tot) {
        int l=findbyrank(pos+1),r=findbyrank(pos+2);
        splay(r,0);splay(l,r);
        int new_node=build(1,tot,l);
        ch[l][1]=new_node;
        update(l);update(r);
        splay(new_node,0);
    }
    void del(int pos,int tot) {
        int l=findbyrank(pos),r=findbyrank(pos+tot+1);
        splay(l,0);splay(r,l);

        recycle(ch[r][0]);
        ch[r][0]=0;

        update(r);update(l);
    }
    void change(int pos,int tot,int p) {
        int l=findbyrank(pos),r=findbyrank(pos+tot+1);
        splay(l,0);splay(r,l);

        key[ch[r][0]]=p;
        tag[ch[r][0]]=1;
        sum[ch[r][0]]=siz[ch[r][0]]*p;
        if(key[ch[r][0]]>=0) {lx[ch[r][0]]=rx[ch[r][0]]=mx[ch[r][0]]=sum[ch[r][0]];}
        else {lx[ch[r][0]]=rx[ch[r][0]]=0;mx[ch[r][0]]=key[ch[r][0]];}
        update(r);update(l);
    }
    void reserve(int pos,int tot) {
        int l=findbyrank(pos),r=findbyrank(pos+tot+1);
        splay(l,0);splay(r,l);

        Rev(ch[r][0]);
        rev[ch[r][0]]^=1;
        update(r);update(l);
    }
    void get(int pos,int tot) {
        int l=findbyrank(pos),r=findbyrank(pos+tot+1);
        splay(l,0);splay(r,l);

        printf("%d\n",sum[ch[r][0]]);
    }
    void query() {printf("%d\n",mx[rt]);}
    void print(int x) {
        if(!x) return;
        pushdown(x);
        print(L);
        printf("%d ",key[x]);
        print(R);
    }
}s;
int n,m;
char str[10];
int read();
void init();
int main() {
    init();
    for(int i=1;i<=m;++i) {
        scanf("%s",str);
        if(str[0]=='I' && str[1]=='N') {
            int pos=read(),tot=read();
            for(int i=1;i<=tot;++i) a[i]=read();
            s.insert(pos,tot);
        }
        else if(str[0]=='D' && str[1]=='E') {
            int pos=read(),tot=read();
            if(tot==0) continue;
            s.del(pos,tot);
        }
        else if(str[0]=='M' && str[1]=='A' && str[2]=='K') {
            int pos=read(),tot=read(),p=read();
            if(tot==0) continue;
            s.change(pos,tot,p);
        }
        else if(str[0]=='R' && str[1]=='E') {
            int pos=read(),tot=read();
            if(tot==0) continue;
            s.reserve(pos,tot);
        }
        else if(str[0]=='G' && str[1]=='E') {
            int pos=read(),tot=read();
            if(tot==0) puts("0");
            else s.get(pos,tot);
        }
        else s.query();
    }
    return 0;
}
int read() {
    int _ans=0,_flag=1;
    char _ch=getchar();
    while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
    if(_ch == '-') {_flag = -1;_ch = getchar();}
    while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
    return _ans*_flag;
}
void init() {
    n=read();m=read();
    s.mx[0]=-INF;
    a[1]=-INF;a[n+2]=-INF;
    for(int i=2;i<=n+1;++i) a[i]=read();
    s.rt=s.build(1,n+2,0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值