将回忆酿成烈酒入喉 从此不再挽留不再回头 |

Altwilio

园龄:3年5个月粉丝:10关注:7

Splay P2042 [NOI2005] 维护数列

开坑!!!

NOI2005 维护数列

1. 题意

维护一个数列,要求支持

  1. INSERTposi 后插入 tot 个数字 c1,c2ctot
  2. DELETE 删除 posi 后连续 tot 个数字。
  3. MAKESAMEposi 后连续 tot 个数字改为 c
  4. REVERSEpositot 个数字翻转。
  5. GETSUM 计算从 positot 个数字的和。
  6. MAXSUM 求出最大子序列和。

2. 思路

考虑 splay 每个节点需要维护的信息。

  1. ms 最大子段和
  2. ls 最大前缀和
  3. rs 最大后缀和
  4. sum 区间和
  5. size 区间节点个数
  6. rev 翻转的懒标记
  7. same 整个区间是否变成根相同的数
  8. s[2],p,v 常规的,左右儿子,祖先,权值

之后考虑,如果一个点有懒标记,节点存储的是懒标记操作之前的值,还是懒标记操作之后的值。本题的操作会影响存储的值,比如 rev 会影响 ls,rs,本题记录懒标记操作之后的值。这样写比较方便。

每次懒标记标记后需要把节点维护的每一个值都算一遍。以保证标记后对应操作后的值仍然正确。对于每次 pushdown 不仅要标记上子节点,还要更新子节点的 14 信息。需要保证子节点的信息正确性,保证子节点推父节点的信息正确。

  • 对于给定区间建立二叉平衡树,如线段树一样递归建立即可。
  • splay 的内存回收机制:开一个数组存可用的下标,初始化为 1n,每次删除将子树的所有节点编号存入该数组,每次插入寻找可用下标即可。

操作 1 实现:

posi 后插入 tot 个数字 c1,c2ctot

L=posi,R=posi+1,将 L 转到根,将 R 转到 L 的下方,这是 R 的左子树是空的,将需要插入的序列建立平衡二叉树插入 R 的左子树,之后 pushup L,R 的信息即可。

操作 2 实现:

删除 posi 后连续 tot 个数字。

L=posi1,R=posi+tot+1,将 L 转到根,将 R 转到 L 的下方,删除 R 的左子树即可。记得 pushup

操作 3 实现:

posi 后连续 tot 个数字改为 c

L=posi1,R=posi+tot+1,将 L 转到根,将 R 转到 L 的下方,将 R 的左子树根节点打上 same 标记即可,记得改变当前节点的信息。

理解以上过程,代码很好写,但是好调就不一定了,在晚自习下课前刚出来了,嗨害嗨

有轻微注释

#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

template<class T> inline void read(T &x){
    x = 0; register char c = getchar(); register bool f = 0;
    while(!isdigit(c)) f ^= c == '-', c = getchar();
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
    if(f) x = -x;
}

template<class T> inline void print(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) print(x / 10);
    putchar('0' + x % 10);
}

const int N = 5e5 + 10;
const int inf = 1e9;

int n, m;
struct Node{
    int s[2], p, v;
    int rev, same;
    int size, sum, ms, ls, rs;
    inline void init(int _v, int _p){ //初始化
        s[0] = s[1] = 0, p = _p, v = _v;
        rev = same = 0;
        size = 1, sum = ms = v;
        ls = rs = max(v, 0);
    }
}tr[N];
int root, nodes[N], tt; //回收站
int w[N];

inline void pushup(int x){ // 注意信息维护全,并且注意顺序
    auto &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
    u.size = l.size + r.size + 1;
    u.sum = l.sum + r.sum + u.v;
    u.ls = max(l.ls, l.sum + u.v + r.ls);
    u.rs = max(r.rs, r.sum + u.v + l.rs);
    u.ms = max(max(l.ms, r.ms), l.rs + u.v + r.ls);
}

inline void pushdown(int x){ //注意细节
    auto &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
    if (u.same){
        u.same = u.rev = 0;
        if (u.s[0]) l.same = 1, l.v = u.v, l.sum = l.v * l.size;
        if (u.s[1]) r.same = 1, r.v = u.v, r.sum = r.v * r.size;
        if (u.v > 0){
            if (u.s[0]) l.ms = l.ls = l.rs = l.sum;
            if (u.s[1]) r.ms = r.ls = r.rs = r.sum;
        }else{
            if (u.s[0]) l.ms = l.v, l.ls = l.rs = 0;
            if (u.s[1]) r.ms = r.v, r.ls = r.rs = 0;
        }
    }else if (u.rev){
        u.rev = 0, l.rev ^= 1, r.rev ^= 1;
        swap(l.ls, l.rs), swap(r.ls, r.rs);
        swap(l.s[0], l.s[1]), swap(r.s[0], r.s[1]);
    }
}

inline void rotate(int x){
    int y = tr[x].p, z = tr[y].p;
    int k = tr[y].s[1] == x;
    tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
    tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
    tr[x].s[k ^ 1] = y, tr[y].p = x;
    pushup(y), pushup(x);
}

inline void splay(int x, int k){
    while (tr[x].p != k){
        int y = tr[x].p, z = tr[y].p;
        if (z != k)
            if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
            else rotate(y);
        rotate(x);
    }
    if (!k) root = x;
}

inline int get_k(int k){
    int u = root;
    while (u){
        pushdown(u);
        if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0];
        else if (tr[tr[u].s[0]].size + 1 == k) return u;
        else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1];
    }
}

inline int build(int l, int r, int p){
    int mid = l + r >> 1;
    int u = nodes[tt -- ]; //根节点从回收站拿一个点
    tr[u].init(w[mid], p); //初始化值,父节点
    if (l < mid) tr[u].s[0] = build(l, mid - 1, u);
    if (mid < r) tr[u].s[1] = build(mid + 1, r, u);
    pushup(u);
    return u;
}

inline void dfs(int u){ //内存回收
    if (tr[u].s[0]) dfs(tr[u].s[0]); //有左回收左
    if (tr[u].s[1]) dfs(tr[u].s[1]); //有右回收右
    nodes[ ++ tt] = u; //回收当前
}

int main(){
    for (int i = 1; i < N; i ++ ) nodes[ ++ tt] = i; //存进回收站
    read(n), read(m);
    tr[0].ms = w[0] = w[n + 1] = -inf;
    for (int i = 1; i <= n; i ++ ) read(w[i]);
    root = build(0, n + 1, 0); //建立二叉平衡树
    char op[20];
    while (m -- ){
        scanf("%s", op);
        if (!strcmp(op, "INSERT")){
            int posi, tot; read(posi), read(tot);
            for (int i = 0; i < tot; i ++ ) read(w[i]);
            int l = get_k(posi + 1), r = get_k(posi + 2); //找到 L,R 因为有哨兵需要+1
            splay(l, 0), splay(r, l); //将L转到根,R到L下方
            int u = build(0, tot - 1, r); //以R为根建树
            tr[r].s[0] = u;
            pushup(r), pushup(l);
        }else if (!strcmp(op, "DELETE")){
            int posi, tot; read(posi), read(tot);
            int l = get_k(posi), r = get_k(posi + tot + 1); //找到L,R 
            splay(l, 0), splay(r, l); //将L转到根,R到L下方
            dfs(tr[r].s[0]);
            tr[r].s[0] = 0;
            pushup(r), pushup(l);
        }else if (!strcmp(op, "MAKE-SAME")){
            int posi, tot, c; read(posi), read(tot), read(c);
            int l = get_k(posi), r = get_k(posi + tot + 1);
            splay(l, 0), splay(r, l);
            auto& son = tr[tr[r].s[0]]; //R的左儿子
            son.same = 1, son.v = c, son.sum = c * son.size; //更新当前节点
            if (c > 0) son.ms = son.ls = son.rs = son.sum;
            else son.ms = c, son.ls = son.rs = 0;
            pushup(r), pushup(l);
        }else if (!strcmp(op, "REVERSE")){
            int posi, tot; read(posi), read(tot);
            int l = get_k(posi), r = get_k(posi + tot + 1);
            splay(l, 0), splay(r, l);
            auto& son = tr[tr[r].s[0]];
            son.rev ^= 1; //打标记
            swap(son.ls, son.rs); //交换前缀最大后缀最大
            swap(son.s[0], son.s[1]); //交换左右子树
            pushup(r), pushup(l);
        }else if (!strcmp(op, "GET-SUM")){
            int posi, tot; read(posi), read(tot);
            int l = get_k(posi), r = get_k(posi + tot + 1);
            splay(l, 0), splay(r, l);
            print(tr[tr[r].s[0]].sum), puts("");
        } else print(tr[root].ms), puts("");
    }
    return 0;
}

本文作者:Altwilio

本文链接:https://www.cnblogs.com/William-Sg/p/16477782.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Altwilio  阅读(18)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起