洛谷 P2042 [NOI2005] 维护数列

Description

P2042 [NOI2005] 维护数列

Solution

一道细节巨多的毒瘤题。

我用的 \(fhq-treap\) 通过的此题。

下面我们以此分析一下。

先说一下 \(fhq-treap\) 中需要存什么东西。

struct Treap{
    int ch[2], siz, val, wei, maxqz, maxhz, maxzd, sum, cov;
    bool lazy, tag;//lazy: 翻转标记 tag: 区间覆盖标记
}t[N];
  • ch[2]: 左右子树。

  • siz: 子树大小。

  • val: 当前节点权值。

  • wei: 随机值。(合并用的,非常玄学)

  • maxqz,maxhz,sum: 最大前缀和,最大后缀和,区间和。(为计算最大子段和做铺垫)

  • maxzd: 最大子段和。

  • cov: 覆盖操作的值。

\(bool\) 类型两个变量上面有注释。

操作就不分析了,直接来看函数吧。

pushup

先放代码。

inline void pushup(int x){
    if(!x) return;
    t[x].siz = t[ls(x)].siz + t[rs(x)].siz + 1;
    t[x].sum = t[ls(x)].sum + t[x].val + t[rs(x)].sum;
    t[x].maxqz = max(max(t[ls(x)].maxqz, t[ls(x)].sum + t[x].val + t[rs(x)].maxqz), 0);
    t[x].maxhz = max(max(t[rs(x)].maxhz, t[rs(x)].sum + t[x].val + t[ls(x)].maxhz), 0);
    t[x].maxzd = max(t[x].val, t[ls(x)].maxhz + t[x].val + t[rs(x)].maxqz);
    if(ls(x)) t[x].maxzd = max(t[x].maxzd, t[ls(x)].maxzd);
    if(rs(x)) t[x].maxzd = max(t[x].maxzd, t[rs(x)].maxzd);
}

看着就心烦

\(siz,sum\) 维护就不多说了。

\(fa_{maxqz} = max(lson_{maxqz}, lson_{sum} + fa_{val} + rson_{maxqz})\),再和 0 取较大值(不理解的话可以自己手模一下)。

\(maxhz\) 同理。

\(fa_{maxzd} = max(fa_{val},lson_{maxhz} + fa_{val} + rson_{maxqz})\)

如果有左右子树的话,就再和左右子树中的最大子段和取最大值。

这样就不难理解了吧,看起来是不是还挺顺眼的。

翻转操作

inline void Reverse(int x){
    if(!x) return;
    swap(ls(x), rs(x));//交换左右孩子
    swap(t[x].maxqz, t[x].maxhz);//交换前缀和后缀
    t[x].lazy ^= 1;//翻转两次等于没翻转,所以异或 1
}

区间覆盖操作

inline void Cover(int x, int k){
    t[x].val = t[x].cov = k, t[x].sum = t[x].siz * k;//当前节点值和覆盖值都赋值为 k,区间和 = 区间大小 * k
    t[x].maxqz = t[x].maxhz = max(0, t[x].sum);//显然如果 k > 0,最大前后缀和就是区间和,反之就是 0
    t[x].maxzd = max(t[x].val, t[x].sum);//跟最大前后缀和差不多,但是由于必须选一个,所以跟 val 取较大值,而不是 0
    t[x].tag = 1;//覆盖标记打为 1
}

删除操作

删除方法一会下面会讲。

inline void Delete(int x){
    if(!x) return;
    stk[++top] = x;//这里一会再说,这句话相当于删除了这个点
    if(ls(x)) Delete(ls(x));//如果有左右子树,就删除
    if(rs(x)) Delete(rs(x));
}

pushdown

inline void pushdown(int x){
    if(!x) return;
    if(t[x].lazy){//如果有翻转标记
        if(ls(x)) Reverse(ls(x));
        if(rs(x)) Reverse(rs(x));
        t[x].lazy = 0;
    }
    if(t[x].tag){//如果有覆盖标记
        if(ls(x)) Cover(ls(x), t[x].cov);
        if(rs(x)) Cover(rs(x), t[x].cov);
        t[x].tag = t[x].cov = 0;
    }
}

比较好理解吧。

分裂 & 合并操作

这两个由于没什么变化就一起说了,\(pushdown\) 随时写着点。

inline void split(int x, int k, int &a, int &b){
    if(!x){
        a = b = 0;
        return;
    }
    pushdown(x);
    if(k >= t[ls(x)].siz + 1){
        a = x;
        split(rs(x), k - t[ls(x)].siz - 1, rs(x), b);
    }else{
        b = x;
        split(ls(x), k, a, ls(x));
    }
    pushup(x);
}

inline int merge(int x, int y){
    if(!x || !y) return x | y;
    if(t[x].wei <= t[y].wei){
        pushdown(x);
        rs(x) = merge(rs(x), y);
        pushup(x);
        return x;
    }else{
        pushdown(y);
        ls(y) = merge(x, ls(y));
        pushup(y);
        return y;
    }
}

建新节点(newnode)

这个就有点意思了。

赋值为 0 的语句一个都不能少(原因一会和上面删除那里一起说)

inline int newnode(int k){
    tot = stk[top--];//这个也一会再说
    t[tot].wei = rand();
    ls(tot) = rs(tot) = t[tot].lazy = t[tot].tag = 0;//这句话千万不能少
    t[tot].val = t[tot].sum = k, t[tot].siz = 1;
    t[tot].maxqz = t[tot].maxhz = max(0, t[tot].sum);
    t[tot].maxzd = max(t[tot].val, t[tot].sum);
    return tot;
}

其它语句跟区间覆盖都差不多,可以看那里理解一下这个。

建树操作(build)

inline int build(int l, int r){
    if(l == r)
        return newnode(p[l]);//p 是输入的数组
    int mid = (l + r) >> 1;
    return merge(build(l, mid), build(mid + 1, r));//返回根
}

各个操作都已经分析完了,我们来解释一下上面的问题。

一句话说:这道题\(\huge{卡空间,卡空间,卡空间!!!}\)

那么我们怎么办呢?

很简单就像上面那样不就完了?

我们开个栈,把不用的点都压进去,使用的时候再弹出来。

这样就有了上面的写法。

删除时:直接压到栈里面。

新建节点时:直接把节点编号赋值成栈顶元素。

然后我们区间覆盖时,如果先全部删除,再插入就会 \(TLE\)

所以我们要用打标记,\(pushdown\) 不停向下处理。

主函数就不用多说了吧,自己看着理解吧。

刚开始的时候把所有的数都加到栈中。

Code

#include <bits/stdc++.h>
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]

using namespace std;

const int N = 5e5 + 10;
int stk[N], top;
int n, m, root, tot;
int p[N];
struct Treap{
    int ch[2], siz, val, wei, maxqz, maxhz, maxzd, sum, cov;
    bool lazy, tag;//lazy: 翻转标记 tag: 区间覆盖标记
}t[N];

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

inline void pushup(int x){
    if(!x) return;
    t[x].siz = t[ls(x)].siz + t[rs(x)].siz + 1;
    t[x].sum = t[ls(x)].sum + t[x].val + t[rs(x)].sum;
    t[x].maxqz = max(max(t[ls(x)].maxqz, t[ls(x)].sum + t[x].val + t[rs(x)].maxqz), 0);
    t[x].maxhz = max(max(t[rs(x)].maxhz, t[rs(x)].sum + t[x].val + t[ls(x)].maxhz), 0);
    t[x].maxzd = max(t[x].val, t[ls(x)].maxhz + t[x].val + t[rs(x)].maxqz);
    if(ls(x)) t[x].maxzd = max(t[x].maxzd, t[ls(x)].maxzd);
    if(rs(x)) t[x].maxzd = max(t[x].maxzd, t[rs(x)].maxzd);
}

inline void Reverse(int x){
    if(!x) return;
    swap(ls(x), rs(x));
    swap(t[x].maxqz, t[x].maxhz);
    t[x].lazy ^= 1;
}

inline void Cover(int x, int k){
    t[x].val = t[x].cov = k, t[x].sum = t[x].siz * k;
    t[x].maxqz = t[x].maxhz = max(0, t[x].sum);
    t[x].maxzd = max(t[x].val, t[x].sum);
    t[x].tag = 1;
}

inline void Delete(int x){
//	cout<<"xx  "<<x<<endl;
    if(!x) return;
    stk[++top] = x;
    if(ls(x)) Delete(ls(x));
    if(rs(x)) Delete(rs(x));
}

inline void pushdown(int x){
    if(!x) return;
    if(t[x].lazy){
        if(ls(x)) Reverse(ls(x));
        if(rs(x)) Reverse(rs(x));
        t[x].lazy = 0;
    }
    if(t[x].tag){
        if(ls(x)) Cover(ls(x), t[x].cov);
        if(rs(x)) Cover(rs(x), t[x].cov);
        t[x].tag = t[x].cov = 0;
    }
}

inline void split(int x, int k, int &a, int &b){
    if(!x){
        a = b = 0;
        return;
    }
    pushdown(x);
    if(k >= t[ls(x)].siz + 1){
        a = x;
        split(rs(x), k - t[ls(x)].siz - 1, rs(x), b);
    }else{
        b = x;
        split(ls(x), k, a, ls(x));
    }
    pushup(x);
}

inline int merge(int x, int y){
    if(!x || !y) return x | y;
    if(t[x].wei <= t[y].wei){
        pushdown(x);
        rs(x) = merge(rs(x), y);
        pushup(x);
        return x;
    }else{
        pushdown(y);
        ls(y) = merge(x, ls(y));
        pushup(y);
        return y;
    }
}

inline int newnode(int k){
    tot = stk[top--];
    t[tot].wei = rand();
    ls(tot) = rs(tot) = t[tot].lazy = t[tot].tag = 0;
    t[tot].val = t[tot].sum = k, t[tot].siz = 1;
    t[tot].maxqz = t[tot].maxhz = max(0, t[tot].sum);
    t[tot].maxzd = max(t[tot].val, t[tot].sum);
    return tot;
}

inline int build(int l, int r){
    if(l == r)
        return newnode(p[l]);
    int mid = (l + r) >> 1;
    return merge(build(l, mid), build(mid + 1, r));
}

int a, b, c;

int main(){
    n = read(), m = read();
    for(int i = 1; i <= 5e5; i++)
        stk[++top] = i;
    for(int i = 1; i <= n; i++)
        p[i] = read();
    root = merge(build(1, n), root);
    char op[10];
    while(m--){
        scanf("%s", op);
        if(op[0] == 'I'){
            int pos = read(), cnt = read();
            split(root, pos, a, b);
            for(int i = 1; i <= cnt; i++)
                p[i] = read();
            root = merge(merge(a, build(1, cnt)), b);
        }else if(op[0] == 'D'){
            int pos = read(), cnt = read();
            split(root, pos - 1, a, b);
            split(b, cnt, b, c);
            Delete(b);
            root = merge(a, c);
        }else if(op[0] == 'M' && op[2] == 'K'){
            int pos = read(), cnt = read(), k = read();
            split(root, pos - 1, a, b);
            split(b, cnt, b, c);
            Cover(b, k);
            root = merge(merge(a, b), c);
        }else if(op[0] == 'R'){
            int pos = read(), cnt = read();
            split(root, pos - 1, a, b);
            split(b, cnt, b, c);
            Reverse(b);
            root = merge(merge(a, b), c);
        }else if(op[0] == 'G'){
            int pos = read(), cnt = read();
            split(root, pos - 1, a, b);
            split(b, cnt, b, c);
            printf("%d\n", t[b].sum);
            root = merge(merge(a, b), c);
        }else printf("%d\n", t[root].maxzd);
    }
    return 0;
}

End

posted @ 2021-08-22 18:41  xixike  阅读(54)  评论(0编辑  收藏  举报