zoukankan      html  css  js  c++  java
  • 树链剖分总结

    树链剖分思想不是很复杂。首先给出几个定义吧:

    • 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子
    • 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子
    • 重边:连接任意两个重儿子的边叫做重边
    • 轻边:剩下的即为轻边
    • 重链:相邻重边连起来的 连接一条重儿子 的链叫重链

    核心思想就是,将一棵树拆成多条链,然后对于每一条链,就用数据结构去维护。
    有个不会证明的性质,就是如果将一颗树拆成多条重链和轻边,那么重链的个数不会超过(log_2n),轻边的边数也不会超过(log_2n)。因为这个性质,很多操作我们可以很高效地完成。这个之后就知道了。

    那么我们如何去拆分一颗树呢?通过两次dfs即可解决。
    首先第一次dfs,我们可以处理出每个结点的深度(deep[u]),以它为根的子树中点的数量(sz[u]),以及每个点的父亲结点(fa[u]),并且可以求出每个点的重儿子(son[u])
    代码如下:

    void dfs1(int u, int pa, int d) {
        deep[u] = d;
        fa[u] = pa;
        sz[u] = 1;
        int mx = -1;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v == pa) continue ;
            dfs1(v, u, d + 1) ;
            sz[u] += sz[v] ;
            if(sz[v] > mx) mx = sz[v], son[u] = v;
        }
    }
    

     
    之后进行第二次dfs,这里我们就需要处理出链了。我们知道,dfs序可以将树结构哈希成线性结构,然后方便我们去维护。其实一般树链剖分都要利用dfs序。但是这里因为我们要维护一条链的信息,所以我们应该在dfs的时候优先选择重儿子,并且维护每条链的顶端结点,方便我们后续的操作。
    优先选择重儿子就可以保证一条链的dfs序是连续的,方便我们之后进行维护。
    第二次dfs的代码如下:

    void dfs2(int u, int topf) {
        id[u] = ++cnt;
        v[cnt] = w[u] ;
        top[u] = topf;
        if(!son[u]) return ;
        dfs2(son[u], topf) ;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v != son[u] && v != fa[u]) dfs2(v, v) ;
        }
    }
    

    这样我们就处理出每条链了,top数组保存的就是每条链的顶端结点。对于两点间的路径,我们就可以利用top来加速。
    以上就是树链剖分的核心部分吧。其余部分就是相应的数据结构去维护信息了。
    现在看起来不是很难,但是以前听都听不懂。。还是以前学习的态度不是很认真吧。

    下面给出一道模板题吧:
     

    [洛谷P3384](https://www.luogu.org/problemnew/show/P3384)
    题目中对于路径的修改和询问,我们就类似于求LCA那样往上面跳就行了,同时在跳的过程中,记得对链上面的信息进行维护。 题目中对于子树的修改和询问,也类似,因为他们的dfs序都是连续的,所以直接通过dfs序来维护信息就好啦。 以下为代码(两次dfs不长,加上线段树就有点长了啊。。):
    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, m, r, mod;
    int v[N];
    int deep[N], son[N], fa[N], top[N], id[N], w[N], sz[N];
    int cnt ;
    int sum[4 * N], lazy[4 * N];
    struct Edge{
        int v, next ;
    }e[N << 1];
    int head[N], tot;
    void adde(int u, int v) {
        e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
        e[tot].v = u; e[tot].next = head[v]; head[v] = tot++;
    }
    void dfs1(int u, int pa, int d) {
        deep[u] = d;
        fa[u] = pa;
        sz[u] = 1;
        int mx = -1;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v == pa) continue ;
            dfs1(v, u, d + 1) ;
            sz[u] += sz[v] ;
            if(sz[v] > mx) mx = sz[v], son[u] = v;
        }
    }
    void dfs2(int u, int topf) {
        id[u] = ++cnt;
        v[cnt] = w[u] ;
        top[u] = topf;
        if(!son[u]) return ;
        dfs2(son[u], topf) ;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v != son[u] && v != fa[u]) dfs2(v, v) ;
        }
    }
    void pushup(int o) {
        sum[o] = (sum[o << 1] + sum[o << 1|1]) % mod ;
    }
    void pushdown(int o, int l, int r) {
        if(lazy[o]) {
            int mid = (l + r) >> 1;
            sum[o << 1] = (sum[o << 1] + lazy[o] * (mid - l + 1)) % mod ;
            sum[o << 1|1] = (sum[o << 1|1] + lazy[o] * (r - mid)) % mod ;
            lazy[o << 1] = (lazy[o << 1] + lazy[o]) % mod ;
            lazy[o << 1|1] = (lazy[o << 1|1] + lazy[o]) % mod ;
            lazy[o] = 0;
        }
    }
    void build(int o, int l, int r) {
        if(l == r) {
            sum[o] = v[l] ;
            return ;
        }
        int mid = (l + r) >> 1;
        build(o << 1, l, mid) ;
        build(o << 1|1, mid + 1, r) ;
        pushup(o) ;
    }
    void update(int L, int R, int o, int l, int r,int val) {
        if(L <= l && r <= R) {
            sum[o] = (sum[o] + (r - l + 1) * val) % mod;
            lazy[o] = (lazy[o] + val) % mod;
            return ;
        }
        pushdown(o, l, r) ;
        int mid = (l + r) >> 1;
        if(L <= mid) update(L, R, o << 1, l, mid, val) ;
        if(R > mid) update(L, R, o << 1|1, mid + 1, r, val) ;
        pushup(o) ;
    }
    int query(int L, int R, int o, int l, int r) {
        if(L <= l && r <= R)
            return sum[o] ;
        pushdown(o, l, r);
        int mid = (l + r) >> 1;
        int ans = 0;
        if(L <= mid) ans = (ans + query(L, R, o << 1, l, mid)) % mod;
        if(R > mid) ans = (ans + query(L, R, o << 1|1, mid + 1, r)) % mod;
        return ans ;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m >> r >> mod;
        memset(head, -1, sizeof(head)) ;
        for(int i = 1; i <= n; i++) cin >> w[i];
        for(int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            adde(u, v);
        }
        dfs1(r, 0, 1) ;
        dfs2(r, r) ;
        build(1, 1, n);
        for(int i = 1; i <= m; i++) {
            int op, x, y, z;
            cin >> op >> x ;
            if(op == 1) {
                cin >> y >> z;
                while(top[x] != top[y]) {
                    if(deep[top[x]] < deep[top[y]]) swap(x, y) ;
                    update(id[top[x]], id[x], 1, 1, n, z) ;
                    x = fa[top[x]] ;
                }
                if(deep[x] < deep[y]) swap(x, y) ;
                update(id[y], id[x], 1, 1, n, z) ;
            } else if(op == 2) {
                cin >> y;
                int ans = 0;
                while(top[x] != top[y]) {
                    if(deep[top[x]] < deep[top[y]]) swap(x, y) ;
                    ans = (ans + query(id[top[x]], id[x], 1, 1, n)) % mod;
                    x = fa[top[x]] ;
                }
                if(deep[x] < deep[y]) swap(x, y) ;
                ans = (ans + query(id[y], id[x], 1, 1, n)) % mod ;
                cout << ans << '
    ';
            } else if(op == 3) {
                cin >> z;
                update(id[x], id[x] + sz[x] - 1, 1, 1, n, z) ;
            } else {
                int ans = 0;
                ans = query(id[x], id[x] + sz[x] - 1, 1, 1, n);
                cout << ans << '
    ';
            }
        }
        return 0;
    }
    
    

    未完待续...

  • 相关阅读:
    如何通过CSS3实现背景图片色彩的梯度渐变
    提升网站页面打开速度的12个建议
    DIV+CSS高手必知的15个CSS常识
    飞机订票-1注册用户脚本
    需求不明的测试
    职业发展-外包公司考虑项
    Linux-Apache+Mysql+PHP+PHPWind(重点Apache+PHP集成环境)
    LR11-更改licence
    Linux-服务排查
    C语言-删除重复字符
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10753322.html
Copyright © 2011-2022 走看看