• [一本通学习笔记] 树链剖分


    loj上只挂了两个无重的题,本来心想水一下,结果被SDOI2011染色那题卡了一发。尽管这题之前做过,但实现很不优美。今天WA的第一发一开始测样例就挂了,强行调试半天交上去一分没有。后来仔细想了想怎样写比较清楚(尽管看起来好像有点冗余),写好以后一路顺风一发就过。从这里也有点感悟吧。

    10138. 「一本通 4.5 例 1」树的统计

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N = 1000005;
    int n, m, q, t1, t2, t3, t4;
    namespace seg {
    int a[N], b[N];
    void pushup(int p) {
        a[p] = max(a[p * 2], a[p * 2 + 1]);
        b[p] = b[p * 2] + b[p * 2 + 1];
    }
    void build(int p, int l, int r, int *src) {
        if (l == r) {
            a[p] = b[p] = src[l];
        } else {
            build(p * 2, l, (l + r) / 2, src);
            build(p * 2 + 1, (l + r) / 2 + 1, r, src);
            pushup(p);
        }
    }
    void modify(int p, int l, int r, int pos, int val) {
        if (l == r) {
            a[p] = b[p] = val;
        } else {
            if (pos <= (l + r) / 2) {
                modify(p * 2, l, (l + r) / 2, pos, val);
            } else {
                modify(p * 2 + 1, (l + r) / 2 + 1, r, pos, val);
            }
            pushup(p);
        }
    }
    int querya(int p, int l, int r, int ql, int qr) {
        if (l > qr || r < ql) {
            return -1e+9;
        }
        if (l >= ql && r <= qr) {
            return a[p];
        } else {
            return max(querya(p * 2, l, (l + r) / 2, ql, qr), querya(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr));
        }
    }
    int queryb(int p, int l, int r, int ql, int qr) {
        if (l > qr || r < ql) {
            return 0;
        }
        if (l >= ql && r <= qr) {
            return b[p];
        } else {
            return queryb(p * 2, l, (l + r) / 2, ql, qr) + queryb(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr);
        }
    }
    }  // namespace seg
    
    namespace tree {
    vector<int> g[N];
    int vis[N], dep[N], siz[N], wson[N], top[N], fa[N], dfn[N], ind;
    void dfs1(int p) {
        vis[p] = 1;
        siz[p] = 1;
        for (int i = 0; i < g[p].size(); i++) {
            int q = g[p][i];
            if (vis[q])
                continue;
            fa[q] = p;
            dep[q] = dep[p] + 1;
            dfs1(q);
            siz[p] += siz[q];
            if (siz[q] > siz[wson[p]])
                wson[p] = q;
        }
    }
    void dfs2(int p) {
        vis[p] = 1;
        dfn[p] = ++ind;
        if (wson[p]) {
            top[wson[p]] = top[p];
            dfs2(wson[p]);
        }
        for (int i = 0; i < g[p].size(); i++) {
            int q = g[p][i];
            if (vis[q])
                continue;
            top[q] = q;
            dfs2(q);
        }
    }
    void presolve() {
        memset(vis, 0, sizeof vis);
        dfs1(1);
        memset(vis, 0, sizeof vis);
        top[1] = 1;
        dfs2(1);
    }
    void modify(int p, int val) { seg::modify(1, 1, n, dfn[p], val); }
    int querya(int p, int q) {
        int ret = -1e+9;
        while (top[p] != top[q]) {
            if (dep[top[p]] < dep[top[q]])
                swap(p, q);
            ret = max(ret, seg::querya(1, 1, n, dfn[top[p]], dfn[p]));
            p = fa[top[p]];
        }
        if (dep[p] < dep[q])
            swap(p, q);
        return max(ret, seg::querya(1, 1, n, dfn[q], dfn[p]));
    }
    int queryb(int p, int q) {
        int ret = 0;
        while (top[p] != top[q]) {
            if (dep[top[p]] < dep[top[q]])
                swap(p, q);
            ret += seg::queryb(1, 1, n, dfn[top[p]], dfn[p]);
            p = fa[top[p]];
        }
        if (dep[p] < dep[q])
            swap(p, q);
        return ret + seg::queryb(1, 1, n, dfn[q], dfn[p]);
    }
    }  // namespace tree
    
    int src[N], tmp[N];
    
    signed main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i < n; i++) {
            cin >> t1 >> t2;
            tree::g[t1].push_back(t2);
            tree::g[t2].push_back(t1);
        }
        tree::presolve();
        for (int i = 1; i <= n; i++) {
            cin >> tmp[i];
        }
        for (int i = 1; i <= n; i++) {
            src[tree::dfn[i]] = tmp[i];
        }
        seg::build(1, 1, n, src);
        cin >> q;
        for (int i = 1; i <= q; i++) {
            string op;
            cin >> op >> t1 >> t2;
            if (op[1] == 'H') {
                tree::modify(t1, t2);
            }
            if (op[1] == 'M') {
                cout << tree::querya(t1, t2) << endl;
            }
            if (op[1] == 'S') {
                cout << tree::queryb(t1, t2) << endl;
            }
        }
    }
    

    10141. 「一本通 4.5 练习 3」染色

    这题的思路很常规,无非就是线段树每个节点除了维护答案多维护左边和右边的颜色,然后由于这个答案可以被快速合并,暴力一下就可以了。
    线段树的部分毕竟都是按顺序的所以很容易。但树链剖分查询的时候由于顺序翻转之类的问题,开个结构体以后事情变得清楚了很多。
    类似的问题还有很多,比如维护最大字段和,加等差数列求和等等,把结点对象稍微封装一下其实就会很容易。当然自己码力太弱才是真原因啊~~

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1000005;
    
    int n, m, t1, t2, t3, t4, src[N], tmp[N];
    
    namespace seg {
    int a[N], al[N], ar[N], tg[N];
    void pushup(int p) {
        al[p] = al[p * 2];
        ar[p] = ar[p * 2 + 1];
        a[p] = a[p * 2] + a[p * 2 + 1] - (al[p * 2 + 1] == ar[p * 2] ? 1 : 0);
    }
    void pushdown(int p) {
        if (tg[p] == 0)
            return;
        tg[p * 2] = tg[p * 2 + 1] = tg[p];
        a[p * 2] = a[p * 2 + 1] = 1;
        al[p * 2] = al[p * 2 + 1] = tg[p];
        ar[p * 2] = ar[p * 2 + 1] = tg[p];
        tg[p] = 0;
    }
    void build(int p, int l, int r, int *src) {
        if (l == r) {
            a[p] = 1;
            al[p] = ar[p] = src[l];
        } else {
            build(p * 2, l, (l + r) / 2, src);
            build(p * 2 + 1, (l + r) / 2 + 1, r, src);
            pushup(p);
        }
    }
    void modify(int p, int l, int r, int ql, int qr, int val) {
        if (l > qr || r < ql)
            return;
        if (l >= ql && r <= qr) {
            tg[p] = al[p] = ar[p] = val;
            a[p] = 1;
        } else {
            pushdown(p);
            modify(p * 2, l, (l + r) / 2, ql, qr, val);
            modify(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, val);
            pushup(p);
        }
    }
    int query(int p, int l, int r, int ql, int qr, int &rl, int &rr) {
        if (l > qr || r < ql)
            return 0;
        if (l >= ql && r <= qr) {
            rl = al[p];
            rr = ar[p];
            return a[p];
        } else {
            pushdown(p);
            int l1 = 0, r1 = 0, l2 = 0, r2 = 0, s1 = 0, s2 = 0;
            s1 = query(p * 2, l, (l + r) / 2, ql, qr, l1, r1);
            s2 = query(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, l2, r2);
            rl = l1;
            rr = r2;
            if (rl == 0)
                rl = l2;
            if (rr == 0)
                rr = r1;
            return s1 + s2 - ((r1 && r1 == l2) ? 1 : 0);
        }
    }
    int query(int ql, int qr, int &rl, int &rr) {
        int tmp = query(1, 1, n, ql, qr, rl, rr);
        // cout<<" query "<<ql<<", "<<qr<<" = "<<tmp<<" : "<<rl<<","<<rr<<endl;
        return tmp;
    }
    }  // namespace seg
    
    namespace tree {
    vector<int> g[N];
    int top[N], dep[N], siz[N], wson[N], fa[N], vis[N], dfn[N], ind;
    void dfs1(int p) {
        vis[p] = 1;
        siz[p] = 1;
        for (int i = 0; i < g[p].size(); i++) {
            int q = g[p][i];
            if (vis[q])
                continue;
            fa[q] = p;
            dep[q] = dep[p] + 1;
            dfs1(q);
            siz[p] += siz[q];
            if (siz[q] > siz[wson[p]])
                wson[p] = q;
        }
    }
    void dfs2(int p) {
        vis[p] = 1;
        dfn[p] = ++ind;
        if (wson[p]) {
            top[wson[p]] = top[p];
            dfs2(wson[p]);
        }
        for (int i = 0; i < g[p].size(); i++) {
            int q = g[p][i];
            if (vis[q])
                continue;
            top[q] = q;
            dfs2(q);
        }
    }
    void presolve() {
        memset(vis, 0, sizeof vis);
        dfs1(1);
        memset(vis, 0, sizeof vis);
        top[1] = 1;
        dfs2(1);
    }
    struct Result {
        int ans = 0, cl = 0, cr = 0;
        void get(int ql, int qr) { ans = seg::query(ql, qr, cl, cr); }
    };
    Result merge(Result a, Result b) {
        Result ret;
        ret.ans = a.ans + b.ans - (a.cr == b.cl ? 1 : 0);
        ret.cl = a.cl;
        ret.cr = b.cr;
        return ret;
    }
    int query(int p, int q) {
        if (dfn[p] > dfn[q])
            swap(p, q);
        Result ret[2];
        int inv = 0;
        while (top[p] != top[q]) {
            if (dep[top[p]] < dep[top[q]])
                swap(p, q), inv ^= 1;
            Result tmp;
            tmp.get(dfn[top[p]], dfn[p]);
            ret[inv] = merge(tmp, ret[inv]);
            p = fa[top[p]];
        }
        if (dep[p] < dep[q])
            swap(p, q), inv ^= 1;
        Result tmp;
        tmp.get(dfn[q], dfn[p]);
        ret[inv] = merge(tmp, ret[inv]);
        swap(ret[0].cl, ret[0].cr);
        Result ans = merge(ret[0], ret[1]);
        return ans.ans;
    }
    void modify(int p, int q, int v) {
        while (top[p] != top[q]) {
            if (dep[top[p]] < dep[top[q]])
                swap(p, q);
            seg::modify(1, 1, n, dfn[top[p]], dfn[p], v);
            p = fa[top[p]];
        }
        if (dep[p] < dep[q])
            swap(p, q);
        seg::modify(1, 1, n, dfn[q], dfn[p], v);
    }
    }  // namespace tree
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> tmp[i];
        for (int i = 1; i < n; i++) {
            cin >> t1 >> t2;
            tree::g[t1].push_back(t2);
            tree::g[t2].push_back(t1);
        }
        tree::presolve();
        for (int i = 1; i <= n; i++) src[tree::dfn[i]] = tmp[i];
        seg::build(1, 1, n, src);
        for (int i = 1; i <= m; i++) {
            char op;
            cin >> op;
            if (op == 'C') {
                cin >> t1 >> t2 >> t3;
                tree::modify(t1, t2, t3);
            } else {
                cin >> t1 >> t2;
                cout << tree::query(t1, t2) << endl;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    二叉排序树 常用函数小结
    二叉树的应用:二叉排序树的删除
    剑指 Offer 32
    剑指 Offer 32
    剑指 Offer 68
    剑指 Offer 28. 对称的二叉树 做题小结
    正则表达式不要背
    剑指 Offer 55
    LeetCode226. 翻转二叉树 做题小结
    Tools | 编程IED/编译器
  • 原文地址:https://www.cnblogs.com/mollnn/p/11622853.html
Copyright © 2020-2023  润新知