树链剖分(Heavy-Light Decomposition)2016.10.12

来源:互联网 发布:罗伯特劳伦斯库恩知乎 编辑:程序博客网 时间:2024/05/17 23:50

参考:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

https://oi.abcdabcd987.com/summary-of-heavy-light-decomposition/

http://blog.csdn.net/acdreamers/article/details/10591443


一、概述

所谓树链剖分,就是把树上的路径进行重链、轻链的划分

它并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已

换句话说,树链剖分只是 xx 数据结构 / 算法在树上的推广


定义 size[v] 为以 v 为根节点的子树节点的个数,引入一些概念

  • 重儿子(Preferred Child):令 u 为 v 的儿子中 size 最大的节点,那么 u 就是 v 的重儿子,一个节点最多只能有一个重儿子
  • 重(zhòng)边(Preferred Edge):连接父亲节点和重儿子的边
  • 重链(Preferred Path):由重边及重边连接的节点构成的链


显然有如下性质:

  1. 如果 (v, u) 不为重边,则 size[u] * 2 < size[v]
  2. 从根节点到某一节点的路径上,重边和轻边的数量都不超过 logn 条


二、解决的问题

维护一棵树,支持下列操作:

链上求和

链上求最值

链上修改

子树修改

子树求和

换根


三、实现

1、首先进行如下定义

size[v]        :以 v 为根节点的子树节点的个数

depth[v]     :节点 v 的深度(根节点的深度为 1)

top[v]          :v 所在链的顶端节点

father[v]     :v 的父亲节点

son[v]        :v 的重儿子结点

id[v]            :v 在线段树中的位置(基于点的重编号)

key[pos]    :线段树 pos 位置上所存储的点(基于点的重编号)


2、分两次 dfs 进行剖分

第一次 dfs:

求出所有的节点的 size、depth、size、son、father


第二次 dfs:

求出所有节点的 top、id 以及线段树所有位置上的点

dfs 过程中保证重链各边在线段树中连续分布

对于非叶子节点 v,显然有 top[son[v]] = top[v],对于 v 的轻儿子 u,有 top[u] = u


修改操作

(1)如果节点 u、v 不在同一条重链上,就不断修改至 u、v 处于同一条重链

(2)如果节点 u、v 在同一条重链上,直接进行修改


求和、求最值操作与修改操作类似


1、HDU 3966 Aragorn's Story

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1#define Left(_id) (_id<<1)#define Right(_id) (_id<<1|1)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 5e4 + 10;int N, M, Q, id_count, _u, _v, a, b, c;int A[maxn], size[maxn], son[maxn], father[maxn], depth[maxn], id[maxn], key[maxn], top[maxn];int head[maxn], to[2*maxn], Next[2*maxn], edge;int tree[4*maxn], lazy[4*maxn];char cmd[5];void Init(void);void add_edge(int u, int v);void dfs_first(int u, int _father, int _depth);void dfs_second(int u, int _top);void build(int low, int high, int _id);void update(int Left, int Right, int add, int low, int high, int _id);void pushdown(int _id);int query(int val, int low, int high, int _id);void change(int u, int v, int add);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    while (scanf("%d %d %d", &N, &M, &Q) != EOF) {        Init();        for (int i = 1; i <= N; ++i) {            scanf("%d", &A[i]);        }        for (int i = 1; i <= M; ++i) {            scanf("%d %d", &_u, &_v);            add_edge(_u, _v), add_edge(_v, _u);        }        dfs_first(1, -1, 1);        dfs_second(1, 1);        build(1, N, 1);        while (Q--) {            scanf("%s", cmd);            if (cmd[0] == 'Q') {                scanf("%d", &a);                printf("%d\n", query(id[a], 1, N, 1));            } else {                scanf("%d %d %d", &a, &b, &c);                if (cmd[0] == 'D') {                    c = -c;                }                change(a, b, c);            }        }    }    return 0;}void change(int u, int v, int add){    while (top[u] != top[v]) {        if (depth[top[u]] < depth[top[v]]) {            swap(u, v);        }        update(id[top[u]], id[u], add, 1, N, 1);        u = father[top[u]];    }    if (depth[u] > depth[v]) {        swap(u, v);    }    update(id[u], id[v], add, 1, N, 1);}int query(int tid, int low, int high, int _id){    if (low == high) {        return tree[_id];    }    pushdown(_id);    int mid = (low + high) >> 1;    if (tid <= mid) {        return query(tid, lson);    } else {        return query(tid, rson);    }}void update(int Left, int Right, int add, int low, int high, int _id){    if (low == Left && high == Right) {        tree[_id] += add, lazy[_id] += add;        return;    }    pushdown(_id);    int mid = (low + high) >> 1;    if (Right <= mid) {        update(Left, Right, add, lson);    } else if (Left > mid) {        update(Left, Right, add, rson);    } else {        update(Left, mid, add, lson);        update(mid+1, Right, add, rson);    }}void pushdown(int _id){    if (lazy[_id]) {        tree[Left(_id)] += lazy[_id], tree[Right(_id)] += lazy[_id];        lazy[Left(_id)] += lazy[_id], lazy[Right(_id)] += lazy[_id];        lazy[_id] = 0;    }}void build(int low, int high, int _id){    tree[_id] = lazy[_id] = 0;    if (low == high) {        tree[_id] = A[key[low]];        return;    }    int mid = (low + high) >> 1;    build(lson);    build(rson);}void dfs_second(int u, int _top){    top[u] = _top;    id[u] = ++id_count;    key[id[u]] = u;    if (son[u] == -1) {        return;    }    dfs_second(son[u], _top);    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != son[u] && v != father[u]) {            dfs_second(v, v);        }    }}void dfs_first(int u, int _father, int _depth){    depth[u] = _depth, father[u] = _father, size[u] = 1;    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != _father) {            dfs_first(v, u, _depth+1);            size[u] += size[v];            if (son[u] == -1 || size[v] > size[son[u]]) {                son[u] = v;            }        }    }}void add_edge(int u, int v){    to[edge] = v, Next[edge] = head[u], head[u] = edge++;}void Init(void){    memset(head, -1, sizeof(head));    memset(son, -1, sizeof(son));    memset(to, -1, sizeof(to));    edge = id_count = 0;}

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1#define Left(_id) (_id<<1)#define Right(_id) (_id<<1|1)typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 5e4 + 10;int N, M, Q, id_count, _u, _v, a, b, c;int A[maxn], size[maxn], son[maxn], father[maxn], depth[maxn], id[maxn], key[maxn], top[maxn];int head[maxn], to[2*maxn], Next[2*maxn], edge;int C[maxn];char cmd[5];void Init(void);void add_edge(int u, int v);void dfs_first(int u, int _father, int _depth);void dfs_second(int u, int _top);int query(int x);inline int lowbit(int x);void update(int x, int add);void change(int u, int v, int add);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    while (scanf("%d %d %d", &N, &M, &Q) != EOF) {        Init();        for (int i = 1; i <= N; ++i) {            scanf("%d", &A[i]);        }        for (int i = 1; i <= M; ++i) {            scanf("%d %d", &_u, &_v);            add_edge(_u, _v), add_edge(_v, _u);        }        dfs_first(1, -1, 1);        dfs_second(1, 1);        memset(C, 0, sizeof(C));        for (int i = 1; i <= N; ++i) {            update(id[i], A[i]), update(id[i]+1, -A[i]);        }        while (Q--) {            scanf("%s", cmd);            if (cmd[0] == 'Q') {                scanf("%d", &a);                printf("%d\n", query(id[a]));            } else {                scanf("%d %d %d", &a, &b, &c);                if (cmd[0] == 'D') {                    c = -c;                }                change(a, b, c);            }        }    }    return 0;}void change(int u, int v, int add){    while (top[u] != top[v]) {        if (depth[top[u]] < depth[top[v]]) {            swap(u, v);        }        update(id[top[u]], add), update(id[u]+1, -add);        u = father[top[u]];    }    if (depth[u] > depth[v]) {        swap(u, v);    }    update(id[u], add), update(id[v]+1, -add);}int query(int x){    int ret = 0;    while (x > 0) {        ret += C[x], x -= lowbit(x);    }    return ret;}void update(int x, int add){    while (x <= maxn) {        C[x] += add, x += lowbit(x);    }}inline int lowbit(int x){    return (x & (-x));}void dfs_second(int u, int _top){    top[u] = _top;    id[u] = ++id_count;    key[id[u]] = u;    if (son[u] == -1) {        return;    }    dfs_second(son[u], _top);    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != son[u] && v != father[u]) {            dfs_second(v, v);        }    }}void dfs_first(int u, int _father, int _depth){    depth[u] = _depth, father[u] = _father, size[u] = 1;    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != _father) {            dfs_first(v, u, _depth+1);            size[u] += size[v];            if (son[u] == -1 || size[v] > size[son[u]]) {                son[u] = v;            }        }    }}void add_edge(int u, int v){    to[edge] = v, Next[edge] = head[u], head[u] = edge++;}void Init(void){    memset(head, -1, sizeof(head));    memset(son, -1, sizeof(son));    memset(to, -1, sizeof(to));    edge = id_count = 0;}


2、SPOJ QTREE - Query on a tree

刚开始少写第 90 行过了样例但是 TLE 到死(还以为是 SPOJ 评测机太慢,各种读入优化,加 inline 没什么用...)

然后又写错第 64 行 WA 到死.

还好最后搞定了。。。

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 1e4 + 10;int t, N, id_count = 0, edge = 0, a, b;int num[maxn], size[maxn], top[maxn], son[maxn], father[maxn], depth[maxn], id[maxn];int data[maxn][3], head[maxn], Next[2*maxn], to[2*maxn];int tree[4*maxn];char cmd[10];void Init(void);void add_edge(int u, int v);void dfs_first(int u, int _father, int _depth);void dfs_second(int u, int _top);void build(int low, int high, int _id);void update(int tid, int val, int low, int high, int _id);inline void push_up(int _id);int query(int Left, int Right, int low, int high, int _id);int slove(int u, int v);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);#endif // __AiR_H    scanf("%d", &t);    while (t--) {        scanf("%d", &N);        Init();        for (int i = 1; i < N; ++i) {            scanf("%d %d %d", &data[i][0], &data[i][1], &data[i][2]);            add_edge(data[i][0], data[i][1]);            add_edge(data[i][1], data[i][0]);        }        dfs_first(1, 1, 1);        dfs_second(1, 1);        for (int i = 1; i < N; ++i) {            if (depth[data[i][1]] < depth[data[i][0]]) {                swap(data[i][1], data[i][0]);            }            num[id[data[i][1]]] = data[i][2];        }        build(2, N, 1);        while (scanf("%s", cmd) && cmd[0] != 'D') {            scanf("%d %d", &a, &b);            if (cmd[0] == 'C') {                update(id[data[a][1]], b, 2, N, 1);            } else {                printf("%d\n", slove(a, b));            }        }    }    return 0;}int slove(int u, int v){    int ans = -INF;    while (top[u] != top[v]) {        if (depth[top[u]] < depth[top[v]]) {            swap(u, v);        }        ans = max(ans, query(id[top[u]], id[u], 2, N, 1));        u = father[top[u]];    }    if (u != v) {        if (depth[u] > depth[v]) {            swap(u, v);        }        ans = max(ans, query(id[u]+1, id[v], 2, N, 1));    }    return ans;}int query(int Left, int Right, int low, int high, int _id){    if (Left == low && Right == high) {        return tree[_id];    }    int mid = (low + high) >> 1;    if (Right <= mid) {        return query(Left, Right, lson);    } else if (Left > mid) {        return query(Left, Right, rson);    } else {        return max(query(Left, mid, lson), query(mid+1, Right, rson));    }}void update(int tid, int val, int low, int high, int _id){    if (low == high) {        tree[_id] = val;        return;    }    int mid = (low + high) >> 1;    if (tid <= mid) {        update(tid, val, lson);    } else {        update(tid, val, rson);    }    push_up(_id);}inline void push_up(int _id){    tree[_id] = max(tree[_id<<1], tree[_id<<1|1]);}void build(int low, int high, int _id){    if (low == high) {        tree[_id] = num[low];        return;    }    int mid = (low + high) >> 1;    build(lson);    build(rson);    push_up(_id);}void dfs_second(int u, int _top){    top[u] = _top;    id[u] = ++id_count;    if (son[u] == -1) {        return;    }    dfs_second(son[u], _top);    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != son[u] && v != father[u]) {            dfs_second(v, v);        }    }}void dfs_first(int u, int _father, int _depth){    father[u] = _father, depth[u] = _depth, size[u] = 1;    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != _father) {            dfs_first(v, u, _depth+1);            size[u] += size[v];            if (son[u] == -1 || size[v] > size[son[u]]) {                son[u] = v;            }        }    }}void Init(void){    memset(head, -1, sizeof(head));    memset(son, -1, sizeof(son));    id_count = edge = 0;}void add_edge(int u, int v){    to[edge] = v, Next[edge] = head[u], head[u] = edge++;}

3、BZOJ 1036 [ZJOI2008] 树的统计Count

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 3e4 + 10;int n, q, a, b, id_count, edge;int w[maxn], size[maxn], son[maxn], father[maxn], depth[maxn], top[maxn], id[maxn], key[maxn];int head[maxn], Next[2*maxn], to[2*maxn];int Max[4*maxn], sum[4*maxn];char cmd[10];void dfs_first(int u, int _father, int _depth);void dfs_second(int u, int _top);void add_edge(int u, int v);void Init(void);void build(int low, int high, int _id);void push_up(int _id);void update(int tid, int val, int low, int high, int _id);int query_max(int Left, int Right, int low, int high, int _id);int query_sum(int Left, int Right, int low, int high, int _id);int slove_max(int u, int v);int slove_sum(int u, int v);int main(){#ifdef __AiR_H    freopen("in.txt", "r", stdin);//    freopen("out.txt", "w", stdout);#endif // __AiR_H    scanf("%d", &n);    Init();    for (int i = 1; i < n; ++i) {        scanf("%d %d", &a, &b);        add_edge(a, b), add_edge(b, a);    }    for (int i = 1; i <= n; ++i) {        scanf("%d", &w[i]);    }    dfs_first(1, 0, 1);    dfs_second(1, 1);    build(1, n, 1);    scanf("%d", &q);    while (q--) {        scanf("%s %d %d", cmd, &a, &b);        if (cmd[0] == 'C') {            update(id[a], b, 1, n, 1);        } else if (cmd[1] == 'M') {            printf("%d\n", slove_max(a, b));        } else {            printf("%d\n", slove_sum(a, b));        }    }    return 0;}int slove_max(int u, int v){    int ans = -INF;    while (top[u] != top[v]) {        if (depth[top[u]] < depth[top[v]]) {            swap(u, v);        }        ans = max(ans, query_max(id[top[u]], id[u], 1, n, 1));        u = father[top[u]];    }    if (depth[u] > depth[v]) {        swap(u, v);    }    ans = max(ans, query_max(id[u], id[v], 1, n, 1));    return ans;}int slove_sum(int u, int v){    int ans = 0;    while (top[u] != top[v]) {        if (depth[top[u]] < depth[top[v]]) {            swap(u, v);        }        ans += query_sum(id[top[u]], id[u], 1, n, 1);        u = father[top[u]];    }    if (depth[u] > depth[v]) {        swap(u, v);    }    ans += query_sum(id[u], id[v], 1, n, 1);    return ans;}int query_sum(int Left, int Right, int low, int high, int _id){    if (low == Left && high == Right) {        return sum[_id];    }    int mid = (low + high) >> 1;    if (Right <= mid) {        return query_sum(Left, Right, lson);    } else if (Left > mid) {        return query_sum(Left, Right, rson);    } else {        return query_sum(Left, mid, lson) + query_sum(mid+1, Right, rson);    }}int query_max(int Left, int Right, int low, int high, int _id){    if (low == Left && high == Right) {        return Max[_id];    }    int mid = (low + high) >> 1;    if (Right <= mid) {        return query_max(Left, Right, lson);    } else if (Left > mid) {        return query_max(Left, Right, rson);    } else {        return max(query_max(Left, mid, lson), query_max(mid+1, Right, rson));    }}void update(int tid, int val, int low, int high, int _id){    if (low == high) {        Max[_id] = val, sum[_id] = val;        return;    }    int mid = (low + high) >> 1;    if (tid <= mid) {        update(tid, val, lson);    } else {        update(tid, val, rson);    }    push_up(_id);}void build(int low, int high, int _id){    if (low == high) {        Max[_id] = sum[_id] = w[key[low]];        return;    }    int mid = (low + high) >> 1;    build(lson), build(rson);    push_up(_id);}void push_up(int _id){    Max[_id] = max(Max[_id<<1], Max[_id<<1|1]), sum[_id] = sum[_id<<1] + sum[_id<<1|1];}void dfs_second(int u, int _top){    top[u] = _top;    id[u] = ++id_count;    key[id_count] = u;    if (son[u] == -1) {        return;    }    dfs_second(son[u], _top);    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != son[u] && v != father[u]) {            dfs_second(v, v);        }    }}void dfs_first(int u, int _father, int _depth){    depth[u] = _depth, father[u] = _father, size[u] = 1;    for (int i = head[u]; ~i; i = Next[i]) {        int v = to[i];        if (v != _father) {            dfs_first(v, u, _depth+1);            size[u] += size[v];            if (son[u] == -1 || size[v] > size[son[u]]) {                son[u] = v;            }        }    }}void add_edge(int u, int v){    to[edge] = v, Next[edge] = head[u], head[u] = edge++;}void Init(void){    memset(head, -1, sizeof(head));    memset(son, -1, sizeof(son));    edge = id_count = 0;}


0 0
原创粉丝点击