树链剖分

树链剖分(重链剖分)中的一些概念:

重儿子:所有儿子中子树节点数量最多的儿子。

轻儿子:除重儿子外的儿子。

重边:父亲和重儿子的边。

轻边:父亲和轻儿子的边。

重链:重边连成的链。

轻链:轻边连成的链。

为什么要使用树链剖分?树链剖分后有一条重要的性质:从根节点到任意节点的路径经过的轻重链数量小于 l o g n logn logn

剖分后使重链上节点的 d f s dfs dfs序连续,使链上的操作转换为区间操作,即可用数据结构(如线段树)维护。跳树链复杂度 l o g n logn logn,数据结构 l o g n logn logn(因题而异),单次查询询问为 l o g 2 n log^2n log2n,总复杂度 n l o g 2 n nlog^2n nlog2n


树链剖分方法:

  1. 第一遍 d f s dfs dfs算出节点深度、子树大小和重儿子。

    void dfs (int v) {
        dep[v] = dep[fa[v]] + 1; sz[v] = 1 ;
        for (int i = head[v]; i; i = nxt[i]) {
            if (to[i] == fa[v]) continue ;
            dfs (to[i]) ;
            sz[v] += sz[to[i]] ;
            if (sz[to[i]] > sz[son[v]]) son[v] = to[i] ;
        }
    }
    
  2. 第二遍 d f s dfs dfs构造 d f s dfs dfs序:先递归重儿子,再递归轻儿子。

    void dfs (int v, int t) {
        pos[id[v] = ++ dfn] = v; top[v] = t ;
        if (son[v]) dfs (son[v], t) ;
        for (int i = head[v]; i; i = nxt[i])
            if (to[i] != fa[v] && to[i] != son[v])
                dfs (to[i], to[i]) ;
    }
    

    其中 t o p top top数组为重链的顶端节点编号, s o n son son数组为节点 v v v的重儿子编号。

  3. 链的更新、查询方法:

    void update (int u, int v) {
        while (top[u] != top[v]) {
            if (dep[top[u]] < dep[top[v]]) swap (u, v) ;
            update (id[top[u]], id[u], 1, 1, dfn, 1) ;
            u = fa[top[u]] ;
        }
        if (dep[u] > dep[v]) swap (u, v) ;
        update (id[u], id[v], 1, 1, dfn, 1) ;
    }
    

    每次使深度深的节点跳top并更新信息。


例题

bzoj1036 ZJOI2008 树的统计Count

题意:给定一棵树,三个操作:把节点 u u u的权值改为 t t t;询问 u u u v v v路径上节点的最大权值;询问 u u u v v v路径上节点的权值和。节点数 n ≤ 30000 n \le 30000 n30000,询问数 m ≤ 200000 m \le 200000 m200000

题解:树链剖分,运用 d f s dfs dfs序将路径询问转化为区间询问,用线段树维护区间最大值、权值和。总复杂度 n l o g 2 n nlog^2n nlog2n

#include <bits/stdc++.h>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
using namespace std;
const int maxn = 3e4 + 10, INF = 0x3f3f3f3f ;
int n ;
int w[maxn], size[maxn], dep[maxn], fa[maxn], pos[maxn], bl[maxn], sz ;
int head[maxn], to[2 * maxn], nxt[2 * maxn], tot = 1 ;
int mx[4 * maxn], sum[4 * maxn] ;
void addEdge (int u, int v) {
    to[++ tot] = v; nxt[tot] = head[u]; head[u] = tot ;
}
void dfs1 (int v) {
    size[v] = 1 ;
    for (int i = head[v]; i; i = nxt[i]) {
        if (to[i] == fa[v]) continue ;
        fa[to[i]] = v ;
        dep[to[i]] = dep[v] + 1 ;
        dfs1 (to[i]) ;
        size[v] += size[to[i]] ;
    }
}
void dfs2 (int v, int chain) {
    int k = 0; sz ++ ;
    pos[v] = sz; bl[v] = chain ;
    for (int i = head[v]; i; i = nxt[i])
        if (dep[to[i]] > dep[v] && size[to[i]] > size[k])
            k = to[i] ;
    if (k == 0) return ;
    dfs2 (k, chain) ;
    for (int i = head[v]; i; i = nxt[i])
        if (dep[to[i]] > dep[v] && to[i] != k)
            dfs2 (to[i], to[i]) ;
}
void pushup (int rt) {
    mx[rt] = max (mx[rt * 2], mx[rt * 2 + 1]) ;
    sum[rt] = sum[rt * 2] + sum[rt * 2 + 1] ;
}
void update (int pos, int val, int l, int r, int rt) {
    if (l == r) {
        mx[rt] = sum[rt] = val ;
        return ;
    }
    int m = (l + r) >> 1 ;
    if (pos <= m) update (pos, val, lson) ;
    else update (pos, val, rson) ;
    pushup (rt) ;
}
int querymx (int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return mx[rt] ;
    int m = (l + r) >> 1, res = -0x7fffffff ;
    if (L <= m) res = max (res, querymx (L, R, lson)) ;
    if (R > m) res = max (res, querymx (L, R, rson)) ;
    return res ;
}
int querysum (int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return sum[rt] ;
    int m = (l + r) >> 1, res = 0 ;
    if (L <= m) res += querysum (L, R, lson) ;
    if (R > m) res += querysum (L, R, rson) ;
    return res ;
}
int solvesum (int x, int y) {
    int res = 0 ;
    while (bl[x] != bl[y]) {
        if (dep[bl[x]] < dep[bl[y]]) swap (x, y) ;
        res += querysum (pos[bl[x]], pos[x], 1, n, 1) ;
        x = fa[bl[x]] ;
    }
    if (pos[x] > pos[y]) swap (x, y) ;
    res += querysum (pos[x], pos[y], 1, n, 1) ;
    return res ;
}
int solvemx (int x, int y) {
    int res = -0x7fffffff ;
    while (bl[x] != bl[y]) {
        if (dep[bl[x]] < dep[bl[y]]) swap (x, y) ;
        res = max (res, querymx (pos[bl[x]], pos[x], 1, n, 1)) ;
        x = fa[bl[x]] ;
    }
    if (pos[x] > pos[y]) swap (x, y) ;
    res = max (res, querymx (pos[x], pos[y], 1, n, 1)) ;
    return res ;
}
int main() {
    cin >> n ;
    for (int i = 1; i < n; i ++) {
        int u, v ;
        scanf("%d%d", &u, &v) ;
        addEdge (u, v); addEdge (v, u) ;
    }
    for (int i = 1; i <= n; i ++) scanf("%d", &w[i]) ;
    dfs1 (1); dfs2 (1, 1) ;
    for (int i = 1; i <= n; i ++) update (pos[i], w[i], 1, n, 1) ;
    int q ;
    cin >> q ;
    while (q --) {
        char op[10]; int a, b ;
        scanf("%s%d%d", op, &a, &b) ;
        if (op[0] == 'C') w[a] = b, update (pos[a], b, 1, n, 1) ;
        else if (op[1] == 'S') printf("%d\n", solvesum (a, b)) ;
        else printf("%d\n", solvemx (a, b)) ;
    }
    return 0 ;
}

bzoj4196 NOI2015 软件包管理器

题意:给定一棵树,每个节点权值为0或1。两个操作:询问根节点到节点 v v v的0的数量,并把根节点到节点 v v v路径上的节点权值标为1;询问节点 v v v子树内1的数量,并把节点 v v v子树内的节点权值标为0。

题解:树链剖分,用 d f s dfs dfs序维护子树信息。线段树维护区间和,支持区间修改、区间查询。总复杂度 n l o g 2 n nlog^2n nlog2n

#include <bits/stdc++.h>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
using namespace std;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (!isdigit (c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit (c)) x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
const int maxn = 1e5 + 10 ;
int n, q, dfn ;
int head[maxn], to[maxn], nxt[maxn], tot = 1 ;
int id[maxn], pos[maxn], sz[maxn], son[maxn], fa[maxn], dep[maxn], top[maxn] ;
int sum[maxn * 4], tag[maxn * 4] ;
void addEdge (int u, int v) {
    to[++ tot] = v; nxt[tot] = head[u]; head[u] = tot ;
}
void dfs (int v) {
    dep[v] = dep[fa[v]] + 1; sz[v] = 1 ;
    for (int i = head[v]; i; i = nxt[i]) {
        if (to[i] == fa[v]) continue ;
        dfs (to[i]) ;
        sz[v] += sz[to[i]] ;
        if (sz[to[i]] > sz[son[v]]) son[v] = to[i] ;
    }
}
void dfs (int v, int t) {
    pos[id[v] = ++ dfn] = v; top[v] = t ;
    if (son[v]) dfs (son[v], t) ;
    for (int i = head[v]; i; i = nxt[i])
        if (to[i] != fa[v] && to[i] != son[v])
            dfs (to[i], to[i]) ;
}
void pushup (int rt) {
    sum[rt] = sum[rt * 2] + sum[rt * 2 + 1] ;
}
void pushdown (int rt, int l, int r) {
    if (tag[rt] != -1) {
        tag[rt * 2] = tag[rt * 2 + 1] = tag[rt] ;
        int m = (l + r) >> 1 ;
        sum[rt * 2] = tag[rt] * (m - l + 1) ;
        sum[rt * 2 + 1] = tag[rt] * (r - m) ;
        tag[rt] = -1 ;
    }
}
void update (int L, int R, int val, int l, int r, int rt) {
    if (L <= l && R >= r) {
        sum[rt] = (r - l + 1) * val ;
        tag[rt] = val ;
        return ;
    }
    pushdown (rt, l, r) ;
    int m = (l + r) >> 1 ;
    if (L <= m) update (L, R, val, lson) ;
    if (R > m) update (L, R, val, rson) ;
    pushup (rt) ;
}
int query (int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return sum[rt] ;
    pushdown (rt, l, r) ;
    int m = (l + r) >> 1, res = 0 ;
    if (L <= m) res += query (L, R, lson) ;
    if (R > m) res += query (L, R, rson) ;
    return res ;
}
int query (int u, int v) {
    int res = 0 ;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap (u, v) ;
        res += query (id[top[u]], id[u], 1, dfn, 1) ;
        u = fa[top[u]] ;
    }
    if (dep[u] > dep[v]) swap (u, v) ;
    res += query (id[u], id[v], 1, dfn, 1) ;
    return res ;
}
void update (int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap (u, v) ;
        update (id[top[u]], id[u], 1, 1, dfn, 1) ;
        u = fa[top[u]] ;
    }
    if (dep[u] > dep[v]) swap (u, v) ;
    update (id[u], id[v], 1, 1, dfn, 1) ;
}
int main() {
    n = read() ;
    for (int i = 2; i <= n; i ++) {
        fa[i] = read() + 1;
        addEdge (fa[i], i) ;
    }
    dfs (1); dfs (1, 1) ;
    memset (tag, -1, sizeof tag) ;
    q = read() ;
    while (q --) {
        char op[20]; int x ;
        scanf("%s%d", op, &x); x ++ ;
        if (op[0] == 'i') {
            printf("%d\n", dep[x] - query (1, x)) ;
            update (1, x) ;
        } else {
            printf("%d\n", query (id[x], id[x] + sz[x] - 1, 1, dfn, 1)) ;
            update (id[x], id[x] + sz[x] - 1, 0, 1, dfn, 1) ;
        }
    }
    return 0 ;
}

bzoj4034 HAOI2015 树上操作

题意:给定一棵树,三个操作:节点 v v v的权值增加 a a a;节点 v v v的子树内所有节点的权值增加 a a a;询问节点 v v v的子树和。

题解:树链剖分,用 d f s dfs dfs序维护子树信息。线段树支持单点修改,区间修改,区间查询。总复杂度 n l o g 2 n nlog^2n nlog2n

#include <bits/stdc++.h>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
using namespace std;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
typedef long long ll ;
const int maxn = 1e5 + 10 ;
int n, m, dfn ;
int head[maxn], to[maxn * 2], nxt[maxn * 2], tot = 1;
int w[maxn], dep[maxn], id[maxn], son[maxn], fa[maxn], top[maxn], sz[maxn] ;
ll sum[maxn * 4], tag[maxn * 4] ;
void addEdge (int u, int v) {
    to[++ tot] = v; nxt[tot] = head[u]; head[u] = tot ;
}
void dfs (int v) {
    dep[v] = dep[fa[v]] + 1; sz[v] = 1 ;
    for (int i = head[v]; i; i = nxt[i]) {
        if (to[i] == fa[v]) continue ;
        fa[to[i]] = v;
        dfs (to[i]) ;
        sz[v] += sz[to[i]] ;
        if (sz[to[i]] > sz[son[v]]) son[v] = to[i] ;
    }
}
void dfs (int v, int t) {
    id[v] = ++ dfn; top[v] = t ;
    if (son[v]) dfs (son[v], t) ;
    for (int i = head[v]; i; i = nxt[i])
        if (to[i] != fa[v] && to[i] != son[v])
            dfs (to[i], to[i]) ;
}
void pushup (int rt) {
    sum[rt] = sum[rt * 2] + sum[rt * 2 + 1] ;
}
void pushdown (int rt, int l, int r) {
    if (tag[rt]) {
        tag[rt * 2] += tag[rt]; tag[rt * 2 + 1] += tag[rt] ;
        int m = (l + r) >> 1 ;
        sum[rt * 2] += tag[rt] * (m - l + 1) ;
        sum[rt * 2 + 1] += tag[rt] * (r - m) ;
        tag[rt] = 0 ;
    }
}
void update (int L, int R, ll val, int l, int r, int rt) {
    if (L <= l && R >= r) {
        sum[rt] += 1ll * (r - l + 1) * val; tag[rt] += val ;
        return ;
    }
    pushdown (rt, l, r) ;
    int m = (l + r) >> 1 ;
    if (L <= m) update (L, R, val, lson) ;
    if (R > m) update (L, R, val, rson) ;
    pushup (rt) ;
}
ll query (int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return sum[rt] ;
    pushdown (rt, l, r) ;
    int m = (l + r) >> 1; ll res = 0 ;
    if (L <= m) res += query (L, R, lson) ;
    if (R > m) res += query (L, R, rson) ;
    return res ;
}
int main() {
    n = read(); m = read() ;
    for (int i = 1; i <= n; i ++) w[i] = read() ;
    for (int i = 1; i < n; i ++) {
        int u = read(), v = read() ;
        addEdge (u, v); addEdge (v, u) ;
    }
    dfs (1); dfs (1, 1) ;
    for (int i = 1; i <= n; i ++) update (id[i], id[i], w[i], 1, dfn, 1) ;
    while (m --) {
        int op = read(), x = read(), a ;
        if (op == 1) {
            a = read() ;
            update (id[x], id[x], a, 1, dfn, 1) ;
        } else if (op == 2) {
            a = read() ;
            update (id[x], id[x] + sz[x] - 1, a, 1, dfn, 1) ;
        } else {
            int y = 1; ll res = 0 ;
            while (top[x] != top[y]) {
                if (dep[top[x]] < dep[top[y]]) swap (x, y) ;
                res += query (id[top[x]], id[x], 1, dfn, 1) ;
                x = fa[top[x]] ;
            }
            if (dep[x] > dep[y]) swap (x, y) ;
            res += query (id[x], id[y], 1, dfn, 1) ;
            printf("%lld\n", res) ;
        }
    }
    return 0 ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值