如何求树上最近公共祖先LCA

如何求树上最近公共祖先LCA

求树上最近公共祖先一般常用的有三种方法

预处理时间复杂度 查询时间复杂度
树上倍增 \(O(nlogn)\) \(O(logn)\)
树链剖分 \(O(n)\) \(O(logn)\)
欧拉序+ST表 \(O(nlogn)\) \(O(1)\)

在一般的使用情景下,前两种做法即可满足大多数的题目要求,相比较而言,树链剖分的求法常数更小,更为优秀(也是我主要使用的方法)。在需要大量查询的题目要求下,可以使用第三种方法。


树链剖分求LCA

主要函数部分

int lca(int x, int y)
{
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

基本原理

​ 使用树链剖分求出top,dep等信息。从给定的两个点中,选择深度更深的一个往上跳,直到两个点的top值相等,此时两个点在同一条链上,取出深度较小的点即为LCA。在最坏情况下,给定的树是一颗满二叉树,此时时间复杂度达到\(O(logn)\)


欧拉序+ST表求LCA

主要代码

const int maxn = 1e5;
struct LCA
{
    int ST[(maxn << 1) + 10][maxbit], dfsn[(maxn << 1) + 10], dep[maxn + 10];
    int lg2[(maxn << 1) + 10];
    int cnt;

    inline void dfs(int p, int fa)
    {
        dfsn[p]    = ++cnt;
        dep[p]     = dep[fa] + 1;
        ST[cnt][0] = p;
        for (auto q : edges[p]) {
            if (q == fa)
                continue;
            dfs(q, p);
            ST[++cnt][0] = p;
        }
    }
    inline void init(int root)
    {
        cnt = 0;
        dfs(root, 0);
        int n = cnt;
        for (int i = 2; i <= n; i++)
            lg2[i] = lg2[i >> 1] + 1;
        for (int j = 1; (1 << j) <= n; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                if (dep[ST[i][j - 1]] < dep[ST[i + (1 << (j - 1))][j - 1]])
                    ST[i][j] = ST[i][j - 1];
                else
                    ST[i][j] = ST[i + (1 << (j - 1))][j - 1];
            }
        }
    }
    inline int query(int u, int v)
    {
        int l = dfsn[u];
        int r = dfsn[v];
        if (l > r)
            swap(l, r);
        int dt = r - l + 1;
        int k  = lg2[dt];
        if (dep[ST[l][k]] <= dep[ST[r - (1 << k) + 1][k]])
            return ST[l][k];
        else
            return ST[r - (1 << k) + 1][k];
    }
};

基本原理

​ 先进行一遍dfs求出每个点的欧拉序,通过欧拉序的性质我们可以知道,两个点之间深度最小的点即为这两点的LCA,所以我们使用ST表进行预处理,预处理时使用dep进行比较(在一般的ST表中通常直接比较权值),这样我们就可以快速\(O(1)\)求出\([l,r]\)之间深度最小的点,即为我们所求的LCA

posted @ 2022-04-28 21:20  icey_z  阅读(134)  评论(0)    收藏  举报