📌  相关文章
📜  使用二元提升技术的 N 叉树中节点的第 K 个祖先(1)

📅  最后修改于: 2023-12-03 14:49:52.502000             🧑  作者: Mango

使用二元提升技术的 N 叉树中节点的第 K 个祖先

在 N 叉树中,一个节点的祖先就是从根节点到该节点路径上的所有节点,包括节点本身。如果想要查找某个节点的第 K 个祖先,可以使用二元提升技术来实现。

什么是二元提升技术

二元提升技术(Binary Lifting)是一种常见的处理树的技术,它建立在动态规划的思想上。使用二元提升技术,可以在 O(N logN) 的时间复杂度内,预处理出每个节点的一些祖先节点,从而快速查询节点的祖先。

使用二元提升技术的 N 叉树

对于一颗 N 叉树,我们可以使用类似于二叉树的方式来预处理每个节点的祖先。假设 f[i][j] 表示节点 i 的 2^j 级祖先节点的编号(这里编号从 1 开始),那么可以使用以下递推式来预处理所有的 f[i][j] 节点:

for(int j = 1; j <= MAX_LOGN; j++) {
    for(int i = 1; i <= n; i++) {
        f[i][j] = f[f[i][j-1]][j-1];
    }
}

在预处理完成后,可以通过以下方式查询节点 x 的第 k 个祖先:

int cur = x;
for(int i = MAX_LOGN; i >= 0; i--) {
    int p = f[cur][i];
    if(p && k >= (1 << i)) {
        cur = p;
        k -= (1 << i);
    }

    if(k == 0) {
        return cur;
    }
}
代码实现

下面是使用二元提升技术的 N 叉树中节点的第 K 个祖先的代码实现,其中 MAX_LOGN 表示预处理的最大次数(通常取 log2(N))。

const int MAX_N = 100000;
const int MAX_LOGN = 17;

vector<int> g[MAX_N + 1];
int f[MAX_N + 1][MAX_LOGN + 1];

void dfs(int u, int p) {
    f[u][0] = p;
    for(int i = 1; i <= MAX_LOGN; i++) {
        f[u][i] = f[f[u][i-1]][i-1];
    }

    for(int v : g[u]) {
        if(v != p) {
            dfs(v, u);
        }
    }
}

int find_kth_ancestor(int x, int k) {
    int cur = x;
    for(int i = MAX_LOGN; i >= 0; i--) {
        int p = f[cur][i];
        if(p && k >= (1 << i)) {
            cur = p;
            k -= (1 << i);
        }

        if(k == 0) {
            return cur;
        }
    }

    return -1;
}

int main() {
    int n, m, root;
    cin >> n >> m >> root;

    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    dfs(root, 0);

    for(int i = 0; i < m; i++) {
        int x, k;
        cin >> x >> k;
        cout << find_kth_ancestor(x, k) << endl;
    }

    return 0;
}
总结

使用二元提升技术可以在 O(N logN) 的时间复杂度内,预处理出 N 叉树中每个节点的一些祖先节点,从而快速查询节点的祖先。在实际应用中,可以使用这种技术来解决许多树上的问题,例如 LCA (最近公共祖先)问题。