跳转至

7 树论

7.1 树上倍增

LCA、树上路径、树上路径边权最值。

constexpr int N = 1e5 + 5;
int dep[N], fa[20][N]; // 2^20 = 1048576
vector<vector<int>> g(N);
void dfs(int x, int f) {
    dep[x] = dep[f] + 1, fa[0][x] = f;
    for (int i = 1; i < 20; ++i)
        fa[i][x] = fa[i-1][fa[i-1][x]];
    for (int y : g[x])
        if (y != f) dfs(y, x);
}
int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 19; ~i; --i)
        if (dep[fa[i][x]] >= dep[y]) x = fa[i][x];
    if (x == y) return y;
    for (int i = 19; ~i; --i)
        if (fa[i][x] != fa[i][y])
            x = fa[i][x], y = fa[i][y];
    return fa[0][x];
}

7.2 dfs序LCA

\(O(n\log{n})\) 预处理,\(O(1)\) 查询。

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 5e5 + 5;
constexpr int K = __lg(N) + 3;
int n, m, s;
int dfn[N], cnt, st[K][N]; 
vector<vector<int>> g(N);
void dfs(int x, int f) {
    dfn[x] = ++cnt;
    st[0][dfn[x]] = f;
    for (int y : g[x])
        if (y != f) dfs(y, x);
}
void build_lca() {
    for (int j = 1; j < K; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            int p1 = st[j-1][i];
            int p2 = st[j-1][i + (1 << (j-1))];
            st[j][i] = dfn[p1] < dfn[p2] ? p1 : p2;
        }
    }
}
int lca(int x, int y) {
    if (x == y) return x;
    if (dfn[x] > dfn[y]) swap(x, y);
    int j = __lg(dfn[y] - dfn[x]);
    int p1 = st[j][dfn[x]+1];
    int p2 = st[j][dfn[y]-(1<<j)+1];
    return dfn[p1] < dfn[p2] ? p1 : p2; 
}
signed main() {
    dfs(s, 0); // s 是根
    build_lca();
    cout << lca(u, v);
}

7.3 虚树

当查询点总数有限时,根据查询点及其LCA建立虚树,然后在虚树上面dp。需要预处理LCA和dfs序

vector<int> g[N], h[N], used; // 原树、虚树、虚树中用到的点
int stk[N], top;

void add_edge(int x, int y) {
    h[x].emplace_back(y);
    used.emplace_back(x);
}

void clear() {
    for (int x : used) {
        h[x].clear();
    }
    used.clear();
}

void build() {
    sort(a+1,a+1+k,[&](int x, int y) {
        return dfn[x] < dfn[y];
    });
    stk[top=1] = 1;
    if (a[1] != 1) stk[++top] = a[1];
    for (int i = 2; i <= k; i++) {
        int c = lca(stk[top], a[i]);
        while (top > 1 and dep[stk[top-1]] >= dep[c]) {
            add_edge(stk[top-1], stk[top]);
            top--;
        }        
        if (c != stk[top]) {
            add_edge(c, stk[top]);
            stk[top] = c;
        }
        stk[++top] = a[i];
    }
    while (top > 1) {
        add_edge(stk[top-1], stk[top]);
        top--;
    }
}