zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    Vue路由机制
    谷歌浏览器打不开应用商店的解决方法
    Vue报错——Component template should contain exactly one root element. If you are using vif on multiple elements, use velseif to chain them instead.
    Vue.js学习之——安装
    Vue使用axios无法读取data的解决办法
    关于localstorage存储JSON对象的问题
    2013年整体计划
    个人喜欢的警语收集
    Linux防火墙的关闭和开启
    Flex修改title 转载
  • 原文地址:https://www.cnblogs.com/ccryolitecc/p/13799690.html
Copyright © 2011-2022 走看看