📜  适用于普通树或n元树的LCA(稀疏矩阵DP方法)(1)

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

适用于普通树或n元树的LCA(稀疏矩阵DP方法)

在树上求LCA是树上最常见的问题,一种求LCA的常用方法是使用Tarjan算法,但是这种方法的时间复杂度为O(N+Q),其中N是树的节点数,Q是查询数。如果我们需要多次查询LCA,或者对于一颗较大的树上进行LCA查询,这种时间复杂度显然太慢了。

在这里,我们介绍一种时间复杂度为O(NlogN+Q)的LCA算法——稀疏矩阵DP方法。这种方法通过预处理出树的深度和祖先节点信息,可以在O(1)的时间内回答一个查询,因此适用于多次查询LCA的场合。

算法流程
  1. 预处理出每个节点的深度$d[i]$和祖先节点$fa[i][j]$,其中$j$表示$2^j$级祖先。

    void dfs(int u, int fa, int depth) {
        d[u] = depth;
        fa[u][0] = fa;
        for (int i = 1; i <= log2(N); ++i) {
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        }
        for (int i = head[u]; ~i; i = e[i].next) {
            int v = e[i].to;
            if (v != fa) dfs(v, u, depth + 1);
        }
    }
    
  2. 对于两个节点$x$和$y$,令$d[x]>d[y]$,先将$x$往上跳$d[x]-d[y]$步,然后起点处两点同时往上跳,直到到达它们的LCA。

    int LCA(int x, int y) {
        if (d[x] < d[y]) swap(x, y); // 令x为深度更深的节点
        for (int i = log2(d[x] - d[y]); i >= 0; --i) {
            if (d[fa[x][i]] >= d[y]) x = fa[x][i]; 
        }
        if (x == y) return x; // 特判
        for (int i = log2(d[x]); i >= 0; --i) {
            if (fa[x][i] != fa[y][i]) {
                x = fa[x][i];
                y = fa[y][i];
            }
        }
        return fa[x][0];
    }
    
时间复杂度

时间复杂度为O(NlogN+Q)。

应用场景

适用于多次查询LCA的场合,或者对于一颗较大的树上进行LCA查询。

参考资料
  1. 《算法竞赛进阶指南》
  2. P3379 【模板】最近公共祖先(LCA)【倍增】 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)