Splay P2042 [NOI2005] 维护数列

开坑!!!

\(\mathscr{NOI2005}\) 维护数列

\(1.\) 题意

维护一个数列,要求支持

  1. \(INSERT\)\(posi\) 后插入 \(tot\) 个数字 \(c_1, c_2 \cdots c_{tot}\)
  2. \(DELETE\) 删除 \(posi\) 后连续 \(tot\) 个数字。
  3. \(MAKE-SAME\)\(posi\) 后连续 \(tot\) 个数字改为 \(c\)
  4. \(REVERSE\)\(posi\)\(tot\) 个数字翻转。
  5. \(GET-SUM\) 计算从 \(posi\)\(tot\) 个数字的和。
  6. \(MAX-SUM\) 求出最大子序列和。

\(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\) 不仅要标记上子节点,还要更新子节点的 \(1-4\) 信息。需要保证子节点的信息正确性,保证子节点推父节点的信息正确。

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

操作 \(1\) 实现:

\(posi\) 后插入 \(tot\) 个数字 \(c_1, c_2 \cdots c_{tot}\)

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

操作 \(2\) 实现:

删除 \(posi\) 后连续 \(tot\) 个数字。

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

操作 \(3\) 实现:

\(posi\) 后连续 \(tot\) 个数字改为 \(c\)

\(L = posi - 1, 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;
}

posted @ 2022-07-14 15:13  Altwilio  阅读(17)  评论(0编辑  收藏  举报