[Template] K-class ancestors (long-chain split)

Title: lxhgww whim - long chain split

Thinking

  Long chain split is split according to a tree for the chain length of the guidelines, and the like sectional trees. It has special features:

  \ (1 \) , starts to jump up from a node, after a long-chain does not exceed \ (\ text {O} ( \ sqrt n) \) times. This is no good cross-section of the tree.

  \ (2 \) , a jump from node upwardly \ (K \) layer, where the length of the jump node long chain \ (> K \) . This point is clear, but it is useful. We can use this \ (\ text {O} ( n \ log n) \) pretreatment, \ (\ text {O} (. 1) \) online query \ (K \) level ancestry.

  Suppose a given \ (K \) , we obtain \ (T = \ lfloor \ log_2 K \ rfloor \) , using a first multiplier array a jump \ (T \) layer, and then jump up from the rest of the node \ (Kt \) layer, we can maintain a long chain and each maintains a long chain length of the upward expansion of the chain from the top of the \ (K \) chain, also if these long chain Tiaowan, directly in the long jump on the chain, or expand directly in the chain jump up on top of the chain, due to the nature of the second point, we will not be able to guarantee that every jump expand out of the chain.

  It is not very clever!

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i)
#define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i)
#define per0(i, a) for (int i = a-1; ~i; --i)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)

const int inf = 0x3fffffff;
const int maxn = 311111;

inline int read() {
    int w = 0; char c;
    while (!isdigit(c = getchar())) ;
    while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ 48), c = getchar();
    return w;
}

int n;

struct Edge {
    int v, nxt;
} e[maxn << 1];
int G[maxn], edges;
void clear() {
    memset(G, -1, sizeof G); edges = 0;
}
void adde(int u, int v) {
    e[edges++] = (Edge){v, G[u]}; G[u] = edges-1;
}

int par[20][maxn], mxd[maxn], son[maxn], dep[maxn], top[maxn]; // 维护2^k的祖先、最深的长链、长脸上的孩子结点、自身深度、链顶。类似于重链剖分
vector<int> up[maxn], chain[maxn]; // 记录长链以及向上跳的链

void dfs1(int u) {
    mxd[u] = dep[u] = dep[par[0][u]] + 1, son[u] = 0;
    for (int i = 1; 1<<i <= n; i++) par[i][u] = par[i-1][par[i-1][u]]; // 倍增
    for (int i = G[u], v; ~i; i = e[i].nxt)
        if ((v = e[i].v) != par[0][u])
            par[0][v] = u, dfs1(v), mxd[v] > mxd[u] && (mxd[u] = mxd[v], son[u] = v); // 与树剖不同的地方在于比较关键字为深度而不是子树大小
}

void dfs2(int u, int tp) {
    top[u] = tp;
    if (son[u]) dfs2(son[u], tp);
    for (int i = G[u], v; ~i; i = e[i].nxt)
        if ((v = e[i].v) != son[u] && v != par[0][u]) dfs2(v, v);
}

int base[maxn]; // 维护log2(n)的下取整

int query(int u, int k) {
    if (!k) return u; // 第一种情况:跳的高度为0。直接返回u
    if (k >= dep[u]) return 0; // 提前判断是否会跳出树,返回0
    u = par[base[k]][u], k ^= 1<<base[k]; // 否则先跳2^base[k],剩余的部分根据长剖的性质直接跳
    return k <= dep[u]-dep[top[u]] ? chain[top[u]][dep[u]-dep[top[u]]-k] : up[top[u]][k-dep[u]+dep[top[u]]]; // 根据跳完是否在这条长链上分类讨论
}

int main() {
    scanf("%d", &n); clear();
    for (int i = 1, u, v; i < n; i++) u = read(), v = read(), adde(u, v), adde(v, u);
    dfs1(1), dfs2(1, 1);
    rep(i, 1, n) if (top[i] == i) { // 处理出来长链和向上跳的k级
        int len = mxd[i] - dep[i]; // 长链的长度为len
        for (int u = i, j = 0; j <= len; u = son[u], j++) chain[i].push_back(u);
        for (int u = i, j = 0; j <= len; u = par[0][u], j++) up[i].push_back(u);
    }
    rep(i, 2, n) base[i] = base[i>>1] + 1;
    for (int ans = 0, Q = read(), u, k; Q; Q--) u = ans ^ read(), k = ans ^ read(), printf("%d\n", ans = query(u, k));
    return 0;
}

Guess you like

Origin www.cnblogs.com/ac-evil/p/12158313.html