• 【LibreOJ139】模板:树链剖分


    Link:
    LibreOJ https://loj.ac/problem/139


    Preliminaries


    树链剖分(重链剖分)的复杂度:
    每次跳轻儿子,子树规模至少缩小一半
    所以最多跳 (lfloor log n floor)

    树剖(重剖)求LCA:
    所以就可以 (O(log n)) 用类似倍增的方法求 LCA
    不过实现起来差别很大!
    树剖的实现很喵喵!!

    真·前置知识:DFS序
    类似于括号序列,不同于前序遍历也不同于欧拉序。
    比如说 结点A 左子树B 右子树C 左子树的左子树D
    前序遍历:ABDC
    DFS序:ABDDBCCA
    欧拉序:ABDDBACCA
    具体的基本操作,参考【笔记】树的DFS序


    Solution


    1 引入

    首先明确一点,有四个操作不代表照搬四个操作就可以
    比如
    在 DFS序 中,修改子树仍然可以变成区间修改;修改路径仍然可以是单点修改
    然后现在查询
    比方说查询子树吧,对于修改子树的贡献直接区间查询就可以统计,对于修改路径的贡献也可以做
    但是查询路径呢?
    还是变成四次单点查询就不行了吧?因为贡献还可能来自路径修改

    2 第一步

    所以路径修改必须被拆出来,
    如果只剩下只有左儿子的链就可以变成单独的多个区间修改?
    emmmm
    不对啊,什么左儿子,又不是二叉树
    所谓的左儿子自己定义就可以,为了方便就直接定义成重儿子。

    3 操作转化

    那就很好处理了
    dfs的时候先走重儿子可以使得同一重链结点编号连续
    于是
    树链修改就是多个区间修改
    树链查询就是多个区间查询
    (实际上实现的时候可以仿照LCA的方法做)
    而子树很简单,仍然是dfs序的经典方法
    子树修改 = 区间修改
    子树查询 = 区间查询

    并不是每条重链都开一个线段树!!
    整体开一个就好。

    4 换根

    不过换根呢?
    换根对路径操作没有一点影响

    但是子树就不一样了:
    可以发现 newroot → point → root 这条路径里面的点的子树在换根操作后,、
    会变成 整棵树 减去 原root下(point往newroot那边的子结点)的子树
    对这个区间进行操作就可以了
    而不在这条路径里的不用这么做,写代码的时候别像我一样忘了。。

    5 制杖时间

    今天lazytag不会写调了一晚上
    马上回去补.jpg


    Code


    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    const int MAXN = 1e5 + 10;
    int n, m, cur_root = 1;
    int tot = 0;
    int head[MAXN], nxt[MAXN], to[MAXN], fa[MAXN];
    #define add_edge(a, b) nxt[++tot] = head[a], head[a] = tot, to[tot] = b
    int wson[MAXN], wson_weight[MAXN], siz[MAXN];
    int in[MAXN], out[MAXN], timestamp = 0;
    int depth[MAXN];
    int infrom[MAXN];
    int tval[MAXN];
    int wlink_top[MAXN];
    void dfs1(int x) {
        for (int i = head[x]; i; i = nxt[i]) {
            depth[to[i]] = depth[x] + 1;
            dfs1(to[i]);
            siz[x] += siz[to[i]];
            if (siz[to[i]] > wson_weight[x]) {
                wson_weight[x] = siz[to[i]];
                wson[x] = to[i];
            }
        }
        ++siz[x];
    }
    void dfs2(int x, int wtp) {
        wlink_top[x] = wtp;
        in[x] = ++timestamp;
        infrom[timestamp] = x;
        if (wson[x])
            dfs2(wson[x], (!wtp) ? wson[x] : wtp);
        for (int i = head[x]; i; i = nxt[i]) {
            if (to[i] == wson[x])
                continue;
            //		dfs2(to[i], 0); ITS WRONG
            dfs2(to[i], to[i]);
        }
        out[x] = timestamp;
    }
    const int SEGM = MAXN << 2;
    int lc[SEGM], rc[SEGM];
    int len[SEGM];
    long long segval[SEGM];
    void pushup(int pos) { segval[pos] = segval[pos << 1] + segval[pos << 1 | 1]; }
    void seg_build(int pos, int l, int r) {
        len[pos] = r - l + 1;
    
        if (l == r) {
            segval[pos] = tval[infrom[l]];
            return;
        }
    
        int mid = l + r >> 1;
        seg_build(pos << 1, l, mid);
        seg_build(pos << 1 | 1, mid + 1, r);
        pushup(pos);
    }
    int lca;
    int get_lca(int u, int v) {
        while (wlink_top[u] != wlink_top[v]) {
            if (depth[wlink_top[u]] < depth[wlink_top[v]])
                swap(u, v);
            u = fa[wlink_top[u]];
        }
        if (depth[u] > depth[v])
            swap(u, v);
        return u;
    }
    long long addtag[SEGM];
    long long pdtmp;
    int llc, rrc;
    void pushdown(int x) {
        if (!addtag[x])
            return;
        pdtmp = addtag[x];
        llc = x << 1, rrc = x << 1 | 1;
        addtag[llc] += pdtmp;
        addtag[rrc] += pdtmp;
        segval[llc] += pdtmp * len[llc];
        segval[rrc] += pdtmp * len[rrc];
        addtag[x] = 0;
    }
    void seg_modify(int pos, int l, int r, int ml, int mr, int modval) {
        if (ml > r || mr < l)
            return;
        if (ml <= l && mr >= r) {
            if (l == r) {
                segval[pos] += modval;
                return;
            }
            addtag[pos] += modval;
            pushdown(pos);
            pushup(pos);
            return;
        }
        pushdown(pos);
        int mid = l + r >> 1;
        seg_modify(pos << 1, l, mid, ml, mr, modval);
        seg_modify(pos << 1 | 1, mid + 1, r, ml, mr, modval);
        pushup(pos);
    }
    long long seg_query(int pos, int l, int r, int ql, int qr) {
        if (ql > r || qr < l)
            return 0;
        if (ql <= l && qr >= r) {
            if (l < r /*!!*/) {
                pushdown(pos);
                pushup(pos);
            }
            return segval[pos];
        }
        pushdown(pos);
        int mid = l + r >> 1;
        return seg_query(pos << 1, l, mid, ql, qr) + seg_query(pos << 1 | 1, mid + 1, r, ql, qr);
    }
    void pth_modify(int u, int v, int k) {
        while (wlink_top[u] != wlink_top[v]) {
            if (depth[wlink_top[u]] < depth[wlink_top[v]])
                swap(u, v);
            seg_modify(1, 1, n, in[wlink_top[u]], in[u], k);
            u = fa[wlink_top[u]];
        }
        if (depth[u] > depth[v])
            swap(u, v);
        seg_modify(1, 1, n, in[u], in[v], k);
    }
    int get_cd(int pos) {
        //	if (u==pos) return -1; done by other functions
        // find the nearest(of pos) cd s.t. current_root to cd to pos to 1
        static int u, lst;
        u = cur_root, lst = cur_root;
        while (wlink_top[u][depth] > pos[depth]) {
            lst = wlink_top[u];
            u = wlink_top[u][fa];
        }
        if (u == pos)
            return lst;
        else
            return infrom[in[u] - depth[u] + depth[pos] + 1];  //***
    }
    int cd;
    void tre_modify(int u, int k) {
        if (u == cur_root) {
            seg_modify(1, 1, n, 1, n, k);
            return;
        } else if (in[u] <= in[cur_root] && out[cur_root] <= out[u]) {
            cd = get_cd(u);
            seg_modify(1, 1, n, in[cd], out[cd], -k);
            seg_modify(1, 1, n, 1, n, k);
        } else {
            seg_modify(1, 1, n, in[u], out[u], k);
        }
    }
    long long pth_query(int u, int v) {
        long long ans = 0;
        while (wlink_top[u] != wlink_top[v]) {
            if (depth[wlink_top[u]] < depth[wlink_top[v]])
                swap(u, v);
            ans += seg_query(1, 1, n, in[wlink_top[u]], in[u]);
            u = fa[wlink_top[u]];
        }
        if (depth[u] > depth[v])
            swap(u, v);
        ans += seg_query(1, 1, n, in[u], in[v]);
        return ans;
    }
    long long tre_query(int u) {
        if (u == cur_root)
            return seg_query(1, 1, n, 1, n);
        else if (in[u] <= in[cur_root] && out[cur_root] <= out[u]) {
            cd = get_cd(u);
            return seg_query(1, 1, n, 1, n) - seg_query(1, 1, n, in[cd], out[cd]);
        } else
            return seg_query(1, 1, n, in[u], out[u]);
    }
    int main() {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> tval[i];
        }
        for (int i = 2; i <= n; ++i)  //!!
        {
            cin >> fa[i];
            add_edge(fa[i], i);
        }
        depth[1] = 1;
        dfs1(1);
        dfs2(1, 1);
        seg_build(1, 1, n);
        cin >> m;
        for (int opt, u, v, k, i = 1; i <= m; ++i) {
            cin >> opt;
            switch (opt) {
                case 1:
                    cin >> u;
                    cur_root = u;
                    break;
                case 2:
                    cin >> u >> v >> k;
                    pth_modify(u, v, k);
                    break;
                case 3:
                    cin >> u >> k;
                    tre_modify(u, k);
                    break;
                case 4:
                    cin >> u >> v;
                    cout << pth_query(u, v) << endl;
                    break;
                case 5:
                    cin >> u;
                    cout << tre_query(u) << endl;
                    break;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    【HDOJ】4982 Goffi and Squary Partition
    【HDOJ】4983 Goffi and GCD
    【算法导论】学习笔记——第7章 快速排序
    【算法导论】学习笔记——第6章 堆排序
    【HDOJ】4956 Poor Hanamichi
    【HDOJ】2492 Ping pong
    【Linux】鸟哥的Linux私房菜基础学习篇整理(十二)
    【Linux】鸟哥的Linux私房菜基础学习篇整理(十一)
    统计硬币
    放大的X
  • 原文地址:https://www.cnblogs.com/ccryolitecc/p/13799690.html
Copyright © 2020-2023  润新知