Luogu·最近公共祖先【LCA】

tech2022-08-03  142

luogu P3379 【模板】最近公共祖先(LCA)

Description--Input--Output--Sample Input--Sample Output--说明--解题思路--代码--

Description–

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。


Input–

第一行包含三个正整数 N,M,SN,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 N-1N−1 行每行包含两个正整数 x, yx,y,表示 xx 结点和 yy 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 MM 行每行包含两个正整数 a, ba,b,表示询问 aa 结点和 bb 结点的最近公共祖先。

Output–

输出包含 MM 行,每行包含一个正整数,依次为每一个询问的结果。


Sample Input–

5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5

Sample Output–

4 4 1 4 4

说明–

对于 30%30% 的数据,N\leq 10N≤10,M\leq 10M≤10。

对于 70%70% 的数据,N\leq 10000N≤10000,M\leq 10000M≤10000。

对于 100%100% 的数据,N\leq 500000N≤500000,M\leq 500000M≤500000。

样例说明:

该树结构如下: 第一次询问:2, 42,4 的最近公共祖先,故为 44。

第二次询问:3, 23,2 的最近公共祖先,故为 44。

第三次询问:3, 53,5 的最近公共祖先,故为 11。

第四次询问:1, 21,2 的最近公共祖先,故为 44。

第五次询问:4, 54,5 的最近公共祖先,故为 44。

故输出依次为 4, 4, 1, 4, 44,4,1,4,4。


解题思路–

LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。 ———来自百度百科

我们先考虑一个一个往上跳,嗯,肯定会T 然后,请出我们的倍增,也就是跳 1,2,4,8,16,32 … 但是在这里我们要反着跳(不然会跳过,跳到了祖先而不是他们的lca),也就是按32,16,8,4,2,1…跳,(跳到lca的儿子那里) 这样,向上跳的次数就会大大减小,时间复杂度为O(nlogn)

终于腾出时间打lca了(TAT),我知道我很菜所以请巨爷们别老是说我了好吧。。。


代码–

#include <iostream> #include <cstdio> using namespace std; int n, m, s, a, b, t; int lg[500005], ls[500005], dep[500005], fa[500005][55]; struct ooo { int x, next; }f[1000005]; void dfs(int xx, int father) { fa[xx][0] = father; dep[xx] = dep[father] + 1; for (int i = 1; i <= lg[dep[xx]]; ++i) fa[xx][i] = fa[fa[xx][i - 1]][i - 1]; for (int i = ls[xx]; i; i = f[i].next) if (f[i].x != father) dfs(f[i].x, xx); } int lca(int xx, int yy) //跳一跳 { if (dep[xx] < dep[yy]) swap(xx, yy); while (dep[xx] > dep[yy]) xx = fa[xx][lg[dep[xx] - dep[yy]] - 1]; //提到同一高度 if (xx == yy) return xx; for (int i = lg[dep[xx]] - 1; i >= 0; --i) if (fa[xx][i] != fa[yy][i]) xx = fa[xx][i], yy = fa[yy][i]; return fa[xx][0]; } int main() { scanf("%d%d%d", &n, &m, &s); for (int i = 1; i < n; ++i) { scanf("%d%d", &a, &b); f[++t] = (ooo) {b, ls[a]}, ls[a] = t; f[++t] = (ooo) {a, ls[b]}, ls[b] = t; } for (int i = 1; i <= n; ++i) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); //预处理倍增 dfs(s, 0); //预处理father和深度 for (int i = 1; i <= m; ++i) { scanf("%d%d", &a, &b); printf("%d\n", lca(a, b)); } return 0; }
最新回复(0)