跳转至

树链剖分

模版
C++
const int N = 100005;
vector<int> adj[N];
int low[N], son[N], fa[N], deep[N];
int top[N], dfn[N], revdfn[N], tot;
void dfs1(int u, int p) {
    deep[u] = deep[p] + 1;
    fa[u] = p;
    low[u] = -1;
    son[u] = 1;
    for (auto &to : adj[u]) {
        if (to == p) continue;
        dfs1(to, u);
        son[u] += son[to];
        if (low[u] == -1 || son[to] > son[low[u]]) {
            low[u] = to;
        }
    }
}
void dfs2(int u, int p) {
    top[u] = p;
    dfn[u] = ++tot;
    revdfn[tot] = u;
    if (low[u] == -1) {
        return;
    }
    dfs2(low[u], p);
    for (auto &to : adj[u]) {
        if (to == low[u] || to == fa[u]) {
            continue;
        }
        dfs2(to, to);
    }
}
int LCA(int x, int y) {
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) {
            swap(x, y);
        }
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) {
        swap(x, y);
    }
    return x;
}
例题

P3384 【模板】重链剖分/树链剖分 - 洛谷

操作一, 树上两节点的路径权值加

操作二, 查询树上两节点的路径权值之和

操作三, 树上以某节点为根的子树内的节点加

操作四, 查询树上以某节点为根的子树的权值和

代码
C++
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int __OneWan_2024 = [](){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    return 0;
}();
const int N = 100005;
vector<int> adj[N];
int low[N], son[N], fa[N], deep[N];
int top[N], dfn[N], revdfn[N], tot;
void dfs1(int u, int p) {
    deep[u] = deep[p] + 1;
    fa[u] = p;
    low[u] = -1;
    son[u] = 1;
    for (auto &to : adj[u]) {
        if (to == p) continue;
        dfs1(to, u);
        son[u] += son[to];
        if (low[u] == -1 || son[to] > son[low[u]]) {
            low[u] = to;
        }
    }
}
void dfs2(int u, int p) {
    top[u] = p;
    dfn[u] = ++tot;
    revdfn[tot] = u;
    if (low[u] == -1) {
        return;
    }
    dfs2(low[u], p);
    for (auto &to : adj[u]) {
        if (to == low[u] || to == fa[u]) {
            continue;
        }
        dfs2(to, to);
    }
}
int LCA(int x, int y) {
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) {
            swap(x, y);
        }
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) {
        swap(x, y);
    }
    return x;
}
int a[N];
int P;
struct Node {
    int L, R;
    i64 sum;
} tr[N << 2];
i64 lazy[N << 2];
void pushup(int p) {
    tr[p].sum = (tr[p << 1].sum + tr[p << 1 | 1].sum) % P;
}
void pushtag(int p, i64 k) {
    tr[p].sum = (tr[p].sum + (tr[p].R - tr[p].L + 1) * k % P) % P;
    lazy[p] = (lazy[p] + k) % P;
}
void pushdown(int p) {
    if (lazy[p] != 0) {
        pushtag(p << 1, lazy[p]);
        pushtag(p << 1 | 1, lazy[p]);
        lazy[p] = 0;
    }
}
void build(int p, int L, int R) {
    tr[p].L = L;
    tr[p].R = R;
    if (L == R) {
        tr[p].sum = a[revdfn[L]];
        return;
    }
    int mid = L + R >> 1;
    build(p << 1, L, mid);
    build(p << 1 | 1, mid + 1, R);
    pushup(p);
}
void modify(int p, int QL, int QR, i64 k) {
    if (QL <= tr[p].L && tr[p].R <= QR) {
        pushtag(p, k);
        return;
    }
    pushdown(p);
    int mid = tr[p].L + tr[p].R >> 1;
    if (QL <= mid) {
        modify(p << 1, QL, QR, k);
    }
    if (QR >= mid + 1) {
        modify(p << 1 | 1, QL, QR, k);
    }
    pushup(p);
}
i64 query(int p, int QL, int QR) {
    if (QL <= tr[p].L && tr[p].R <= QR) {
        return tr[p].sum;
    }
    pushdown(p);
    int mid = tr[p].L + tr[p].R >> 1;
    i64 res = 0;
    if (QL <= mid) {
        res = (res + query(p << 1, QL, QR)) % P;
    }
    if (QR >= mid + 1) {
        res = (res + query(p << 1 | 1, QL, QR)) % P;
    }
    return res;
}
void addPath(int x, int y, int k) {
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) {
            swap(x, y);
        }
        modify(1, dfn[top[x]], dfn[x], k);
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) {
        swap(x, y);
    }
    modify(1, dfn[x], dfn[y], k);
}
i64 queryPath(int x, int y) {
    i64 res = 0;
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) {
            swap(x, y);
        }
        res = (res + query(1, dfn[top[x]], dfn[x])) % P;
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) {
        swap(x, y);
    }
    res = (res + query(1, dfn[x], dfn[y])) % P;
    return res;
}
void addSon(int x, int k) {
    modify(1, dfn[x], dfn[x] + son[x] - 1, k);
}
i64 querySon(int x) {
    return query(1, dfn[x], dfn[x] + son[x] - 1);
}
int main() {
    int n, m, rt;
    cin >> n >> m >> rt >> P;
    for (int i = 1 ; i <= n ; i++) {
        cin >> a[i];
    }
    for (int i = 1 ; i < n ; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    dfs1(rt, 0);
    dfs2(rt, rt);
    build(1, 1, n);
    for (int i = 1 ; i <= m ; i++) {
        int op;
        cin >> op;
        if (op == 1) {
            int x, y, z;
            cin >> x >> y >> z;
            addPath(x, y, z);
        } else if (op == 2) {
            int x, y;
            cin >> x >> y;
            cout << queryPath(x, y) % P << "\n";
        } else if (op == 3) {
            int x, z;
            cin >> x >> z;
            addSon(x, z);
        } else {
            int x;
            cin >> x;
            cout << querySon(x) % P << "\n";
        }
    }
    return 0;
}