📜  须藤放置[1.4] |跳跃子树(1)

📅  最后修改于: 2023-12-03 15:28:55.493000             🧑  作者: Mango

须藤放置[1.4] | 跳跃子树

简介

须藤放置[1.4]是一种高效计算树的信息的数据结构,旨在解决树的一些常见问题,如子树和、LCA查询、路径信息等。

跳跃子树是须藤放置中的一个重要概念,指的是将一个节点的所有子孙节点分成若干个块,每个块大小不超过 $\sqrt{n}$($n$为节点的总数),并将这些块用一棵特殊的树(跳跃树)组织起来。

使用
初始化

首先,需要定义一个结构体表示树的节点信息,一般包括父节点、子节点、深度等基本属性,同时还需要定义一个节点的额外信息,如子树和、路径和等。这个结构体的定义可以根据任务需求而定。

struct Node {
    int fa, dep, sz, son, top;
    // 子树信息
    int sum, lazy;
    // 其他信息
};

定义完结构体后,需要将子树分块,根据块的大小计算出需要多少块。这里以设定块大小为 $\sqrt{n}$ 为例:

int sz[maxn], blo[maxn], blk, num;
void dfs1(int u) {
    sz[u] = 1;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (v == fa[u]) continue;
        fa[v] = u, dep[v] = dep[u] + 1;
        dfs1(v);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int tp) {
    blo[u] = tp;
    if (!tp) tp = ++num;
    if (!son[u]) return;
    dfs2(son[u], tp);
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, 0);
    }
}
跳跃树

接下来,需要建立跳跃树,将所有块按照深度从浅到深插入,每个块的父亲为其同层相邻的块中最浅的一个。

struct Tree {
    int l, r, ct, len;
    // 其他信息
} tree[maxn];
void build(int rt, int L, int R) {
    tree[rt].l = L, tree[rt].r = R, tree[rt].ct = rt, tree[rt].len = R - L + 1;
    if (L == R) return;
    int mid = L + R >> 1;
    if (mid - L + 1 <= blk) build(rt, L, mid), build(rt + (mid - L + 1), mid + 1, R);
    else {
        build(rt + 1, L, mid);
        for (int i = L; i <= R; i += blk) build(rt + (i - L + 1) / blk, i, min(R, i + blk - 1));
    }
}
void insert() {
    for (int i = num, lca; i > 1; i--) {
        lca = LCA(tree[i].l, tree[i].r);
        if (blo[lca] == blo[tree[i].l]) tree[i].ct = tree[i + 1].ct, tree[i].len = tree[i + 1].len;
        else {
            int k = ++num;
            if (!tree[i].len) tree[i].len = lca == tree[i].l ? dep[tree[i].r] : dep[tree[i].l];
            int fa = (i + tree[i + 1].ct) >> 1;
            int l = tree[i].l, r = (blo[l] == blo[lca] ? lca - 1 : lca);
            tree[num] = (Tree){l, r, fa, dep[r] - dep[l] + 1};
        }
    }
}
跳跃查询

跳跃树建立之后,就可以使用跳跃树查询节点信息、子树信息、路径信息等了。

int query(int u) {
    int res = 0;
    while (blo[u]) {
        int fa = tree[blo[u]].ct;
        if (fa) res += tree[fa].sum + (tree[fa].len - tree[blo[u]].len) * tree[fa].lazy;
        else res += tree[blo[u]].sum + (dep[u] - dep[tree[blo[u]].l]) * tree[blo[u]].lazy;
        u = fa ? tree[fa].l - 1 : fa;
    }
    return res + Tree[1].sum;
}
void modify(int x, int y, int val, int op) {
    while (blo[x] != blo[y]) {
        if (dep[x] < dep[y]) swap(x, y);
        int fa = tree[blo[x]].ct;
        if (op) add(tree[blo[x]].l, x, val), add(tree[fa].l, x, -val);
        else dec(tree[blo[x]].l, x, val), dec(tree[fa].l, x, -val);
        x = fa ? tree[fa].l - 1 : fa;
    }
    if (op) add(x, y, val);
    else dec(x, y, val);
}
参考资料
  1. Codeforces - 初详细教材
  2. bzc2008's blog - 详细推导 + 代码实现