[NOI2005] 维护数列

[NOI2005] 维护数列

一道锻炼代码能力的好(毒瘤)题!

历经 \(10h+\) , 终于过了这道题...用指针的人太少了, 只能自己查错, 而且由于空指针的存在, 指针本身需要特判的情况就更多. 但是无奈对于指针の爱...唉~

这道题本身没有任何思维难度, 只要会一棵趁手的, 支持区间反转的平衡树就够了, 这里我选择我最爱的, 最容易理解也最容易写的 \(FHQ\ Treap\) .

本身没有难度, 我就不多说了, 但是由于支持的操作较为蛋疼, 加上指针党比较少, 所以.

指针党看这里!!!

这里主要是针对指针党, 因为指针党数量少, 而且指针本身需要注意的就更多, 所以嘛...

空指针

不管是什么操作, 在不保证当前节点不是空指针时, 一定要特判空指针, 一定要特判空指针!!! 尤其是取左儿子大小时, 这个格外容易忽视.

更新区间最大字段和

重点来了!!!

这个卡我好几个小时的地方, 就是这里.

\(code:\)

    void Maintain() {
        s = 1, sum = msum = v, lsum = rsum = max(v, 0);
        if (ls != NULL && rs != NULL) {
            s += ls->s + rs->s, sum += ls->sum + rs->sum;
            lsum = max(ls->lsum, ls->sum + v + rs->lsum);
            rsum = max(rs->rsum, rs->sum + v + ls->rsum);
            msum = max(max(ls->msum, rs->msum), ls->rsum + v + rs->lsum);
        }
        else if (ls != NULL) {
            s += ls->s, sum += ls->sum;
            msum = max(ls->msum, ls->rsum + v);
            lsum = max(ls->lsum, ls->sum + v);
            rsum = max(ls->rsum + v, 0);
        }
        else if (rs != NULL) {
            s += rs->s, sum += rs->sum;
            msum = max(rs->msum, rs->lsum + v);
            lsum = max(rs->lsum + v, 0);
            rsum = max(rs->rsum, rs->sum + v);
        }
    }

没错就是这里, 这个题最难搞的莫过于这个区间最大子段和, 而指针由于空指针的存在, 我们经常会忘记更新 \(msum, lsum, rsum\) 这几个值, 所以每次更新的时候记得先赋初值, 这样就不用担心空指针啦~

需要注意的是, 这里是不能选空段的, 所以 \(msum\) 的初值一定要是当前节点的值, 处理的时候也是, 但是 \(lsum\) 和 \(rsum\) 是可以滴, 因为我们在合并的时候会默认加上当前节点的值, 所以即使 \(lsum\) 和 \(rsum\) 都是空的, 那我们的 \(msum\) 也是有一个当前节点的.

区间反转

\(Reserve\) 的时候记得把 \(lsum, rsum\) 也给 \(Swap\) 了, 因为左右儿子换了, 所以左右最大前缀和也得交换.

插入

出于效率, 我们每次插入一个片段的时候, 不要一个一个的插, 这样死慢, 也不要新建一棵树然后一个一个插再 \(Merge\) , 这样虽然 \(O2\) 可以过, 但是常熟极大, 所以这里最建议的写法是这样的.

\(code:\)

Node *Build(int l, int r) {
    Node *o;
    if (l == r) return o = new Node(a[l]);
    int mid = l + r >> 1;
    return Merge(Build(l, mid), Build(mid + 1, r));
}
void Insert(int pos, int k) {
    Node *x, *y, *o;
    for (int i = 1; i <= k; i++) a[i] = read();
    o = Build(1, k);
    Split(Root, pos, x, y);
    Root = Merge(Merge(x, o), y);
}

这样可以有较高的效率.

内存回收

指针党嘛, 既然有它的坏处, 那就一定有他的好处, 我们的内存回收是相当的好写, 这里不建议使用内存池, 尽管常数上可能会优一些, 但是我们内存回收会难搞一些, 我们用 \(new\) 新建节点, 用 \(delete\) 删除节点.

\(code:\)

void Recycle(Node* o) {
    if (o == NULL) return;
    if (o->ls != NULL) Recycle(o->ls);
    if (o->rs != NULL) Recycle(o->rs);
    delete o;
}
void Delete(int pos, int k) {
    Node *x, *y, *z;
    Split(Root, pos - 1, x, y);
    Split(y, k, y, z);
    Recycle(y);
    Root = Merge(x, z);
}

是不是很简洁? 数组还需要开栈, 我们只需要 \(new\) 和 \(delete\) 就行啦!

总结

到这里, 注意事项就差不多了, 写数据结构嘛, 一定要有一颗良好的心态, 你看我笑着写, 欸嘿~ 欸嘿~ (bushi) .

总而言之, 是一道好(毒瘤)题.

\(code:\)

#include <bits/stdc++.h>
using namespace std;
int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
struct Node {
    Node *ls, *rs;
    int v, s, r, rvs, chg, sum, lsum, rsum, msum;
    bool hv;
    Node(int x) {
        v = x, s = 1, r = rand();
        ls = rs = NULL;
        rvs = chg = hv = 0;
        sum = msum = x, lsum = rsum = max(x, 0);
    }
    void Maintain() {
        s = 1, sum = msum = v, lsum = rsum = max(v, 0);
        if (ls != NULL && rs != NULL) {
            s += ls->s + rs->s, sum += ls->sum + rs->sum;
            lsum = max(ls->lsum, ls->sum + v + rs->lsum);
            rsum = max(rs->rsum, rs->sum + v + ls->rsum);
            msum = max(max(ls->msum, rs->msum), ls->rsum + v + rs->lsum);
        }
        else if (ls != NULL) {
            s += ls->s, sum += ls->sum;
            msum = max(ls->msum, ls->rsum + v);
            lsum = max(ls->lsum, ls->sum + v);
            rsum = max(ls->rsum + v, 0);
        }
        else if (rs != NULL) {
            s += rs->s, sum += rs->sum;
            msum = max(rs->msum, rs->lsum + v);
            lsum = max(rs->lsum + v, 0);
            rsum = max(rs->rsum, rs->sum + v);
        }
    }
    void Update(int x) {
        v = chg = x, sum = s * x, hv = 1;
        msum = max(sum, x), lsum = rsum = max(sum, 0);
    }
    void Swap() {
        rvs ^= 1; swap(ls, rs); swap(lsum, rsum);
    }
    void Pushdown() {
        if (rvs) {
            if (ls != NULL) ls->Swap();
            if (rs != NULL) rs->Swap();
            rvs ^= 1;
        }
        if (hv) {
            if (ls != NULL) ls->Update(chg);
            if (rs != NULL) rs->Update(chg);
            hv = 0;
        }
        Maintain();
    }
} *Root;
void Split(Node* o, int k, Node* &l, Node* &r) {
    if (o == NULL) {
        l = r = NULL; return;
    }
    o->Pushdown();
    int ls = o->ls == NULL ? 1 : o->ls->s + 1;
    if (k >= ls) {
        l = o; Split(o->rs, k - ls, o->rs, r);
    }
    else {
        r = o; Split(o->ls, k, l, o->ls);
    }
    o->Maintain();
}
Node *Merge(Node* l, Node* r) {
    if (l == NULL || r == NULL) return l == NULL ? r : l;
    if (l->r < r->r) {
        l->Pushdown();
        l->rs = Merge(l->rs, r);
        l->Maintain(); return l;
    }
    else {
        r->Pushdown();
        r->ls = Merge(l, r->ls);
        r->Maintain(); return r;
    }
}
void Recycle(Node* o) {
    if (o == NULL) return;
    if (o->ls != NULL) Recycle(o->ls);
    if (o->rs != NULL) Recycle(o->rs);
    delete o;
}
int a[500005];
Node *Build(int l, int r) {
    Node *o;
    if (l == r) return o = new Node(a[l]);
    int mid = l + r >> 1;
    return Merge(Build(l, mid), Build(mid + 1, r));
}
void Insert(int pos, int k) {
    Node *x, *y, *o;
    for (int i = 1; i <= k; i++) a[i] = read();
    o = Build(1, k);
    Split(Root, pos, x, y);
    Root = Merge(Merge(x, o), y);
}
void Delete(int pos, int k) {
    Node *x, *y, *z;
    Split(Root, pos - 1, x, y);
    Split(y, k, y, z);
    Recycle(y);
    Root = Merge(x, z);
}
void Change(int pos, int k) {
    Node *x, *y, *z;
    Split(Root, pos - 1, x, y);
    Split(y, k, y, z);
    if (y != NULL) y->Update(read());
    Root = Merge(Merge(x, y), z);
}
void Reverse(int pos, int k) {
    Node *x, *y, *z;
    Split(Root, pos - 1, x, y);
    Split(y, k, y, z);
    if (y != NULL) y->Swap();
    Root = Merge(Merge(x, y), z);
}
void Get_Sum(int pos, int k) {
    Node *x, *y, *z;
    Split(Root, pos - 1, x, y);
    Split(y, k, y, z);
    printf("%d\n", y == NULL ? 0 : y->sum);
    Root = Merge(Merge(x, y), z);
}
void Max_Sum() {
    printf("%d\n", Root->msum);
}
char opt[20];
int main() {
    int n = read(), m = read(), pos, k;
    Insert(1, n);
    while (m--) {
        scanf("%s", opt);
        if(opt[2] != 'X') pos = read(), k = read();
        switch (opt[2]) {
            case 'S': //INSERT
                Insert(pos, k);
                break;
            case 'L': //DELETE
                Delete(pos, k);
                break;
            case 'K': //MAKE-SAME
                Change(pos, k);
                break;
            case 'V': //REVERSE
                Reverse(pos, k);
                break;
            case 'T': //GET-SUM
                Get_Sum(pos, k);
                break;
            case 'X': //MAX-SUM
                Max_Sum();
                break;
        }
    }
    return 0;
}

不懂指针的同志可以学一下, 如果懂指针的同志, 也可以学习一下这种函数式编程, 用构造函数写数据结构真的是一种享受~ 指针 + 构造函数真的太美了!

上一篇:P4513 小白逛公园


下一篇:hdu 1540 Tunnel Warfare (线段树维护一维联通区间)