长链剖分 学习笔记【咕】
2024-10-21

半端な生命の関数を / 少々ここらでオーバーライド / …したい所だけど現実は / そうそう上手くはいかないようで


把重链剖分选的 siz 最大的儿子换成高度最大的儿子就是长剖了。同样具有一些美妙的性质。

  • 任意点到根节点经过的链数最劣为 \(\sqrt n\)

    就、挺显然的,考虑构造一条经过了 \(\sqrt n+1\) 条链的路径,会发现无论如何都构造不出来。

    这点也决定了长剖和重剖走的不是一条赛道,更多的应该是处理一些和深度有关的神秘场景下的问题?有的博客说可以解决一些子树问题(类似于 dsu on tree 那样的)?(存疑)

  • 任意点 \(x\)\(k\) 级祖先 \(fa\) 所在链长 \(\ge k\)

    考虑反证。若 \(fa\) 所在链长度 \(<k\),则 \(fa\to x\) 更优,矛盾。故原命题成立。


长剖的一个典型应用是 \(O(n\log n)-O(1)\)\(k\) 级祖先。先考虑相对暴力的做法,可以 \(O(n\sqrt n)\) 预处理每个点的 \(1\sim \sqrt n\) 级祖先然后块间块内随便跳一跳就是 \(O(\sqrt n)\) 的单次查询了。

把这个暴力结合「任意点 \(k\) 级祖先所在链长 \(\ge k\)」的性质,有一个很神奇(疯癫)的做法:令 \(r=\dfrac k2\),那么有 \(x\)\(r\) 级祖先所在链长 \(\ge r\),假设我们现在已经知道 \(r\) 级祖先 \(fa_r\),预处理出链内点序列、链顶向上的链长个祖先(这个摊出来是 \(O(n)\) 的),就可以直接 \(\mathcal O(1)\) 访问到 \(fa_k\)

那么问题来了,怎么找到 \(fa_r\) 呢?这看似是个递归的问题,实际上我们发现 \(r\) 的取值只需要 \(\ge \dfrac k2\) 即可,所以又有一个很神奇(疯癫)的做法,我们对于 \(1\sim n\) 的每一个 \(k\),找到其最高二进制位 \(r\)(显然对于所有 \(k\),不同 \(r\) 的个数为 \(\log n\)),对于每个点,预处理出其 \(\log n\) 个不同的 \(r\) 级祖先。显然这个就是我们很熟悉的倍增了。

for (int i = 1, mx = 0; i <= n; ++i) {
    if (i >= (1 << mx) * 2)
        ++mx;
    to[i] = mx;
}
std::vector<std::array<int, 21> > fa(n + 1);
std::vector<int> h(n + 1, 1), son(n + 1), dep(n + 1);
h[0] = 0;
std::function<void(int)> DFS = [&](int x) {
    for (auto i : g[x])
        if (i != fa[x][0]) {
            fa[i][0] = x;
            for (int j = 1; j <= 20; ++j)
                fa[i][j] = fa[fa[i][j - 1]][j - 1];
            dep[i] = dep[x] + 1;
            DFS(i);
            if (h[i] >= h[son[x]])
                son[x] = i, h[x] = h[i] + 1;
        }
    return;
};
dep[1] = 1, DFS(1);
std::vector<int> top(n + 1), id(n + 1);
std::vector<std::vector<int> > anc(n + 1), des(n + 1, std::vector<int> (1));
std::function<void(int, int)> DFS1 = [&](int x, int toq) {
    top[x] = toq;
    if (son[x])
        DFS1(son[x], toq);
    for (auto i : g[x])
        if (i != fa[x][0] && i != son[x])
            DFS1(i, i);
    des[toq].push_back(x);
    id[x] = (int)des[toq].size() - 1;
    if (x == toq) {
        anc[x].push_back(x);
        for (int j = 1, now = x; j <= id[x]; ++j, now = fa[now][0])
            anc[x].push_back(fa[now][0]);
    }
    return;
};
DFS1(1, 1);
auto ask = [&](int x, int k) {
    int x1 = x;
    if (!k)
        return x;
    x = fa[x][to[k]];
    if (dep[x] - dep[top[x]] >= k - (1 << to[k]))
        return des[top[x]][id[x] + k - (1 << to[k])];
    return anc[top[x]][k - (1 << to[k]) - (dep[x] - dep[top[x]])];
};

一言 - Hitokoto