原本计划上个星期写这篇博客,但是被耽误了一周。今晚来填坑。首先来介绍最近公共祖先(LCA)的概念:最近公共祖先_百度百科。这里我们介绍利用倍增求LCA。
先来了解一下倍增的思想:倍增简介_博客。
假设询问的两点为u、v,对于如何求最近公共祖先,倍增算法的思想是这样的:
(1)预处理得到每个节点的二次祖先。
(2)如果u、v不在同一深度,则将u、v移动到同一深度。
(3)采用二分逼近的思想,将u、v的指针同步移动到最远的非公共祖先。
(4)特判此时u == v是否成立,成立则输出结果。不成立则LCA为u或v的父亲节点。
以洛谷P3379为例,代码如下:
#include <iostream> #include <cstdio> using namespace std; const int DEEP = 20; const int MAXN = 10001; struct LINE { int p; int next; }; struct TREE { int d; int f[DEEP]; }; LINE l[MAXN * 2]; TREE t[MAXN]; int h[MAXN]; int n, m, s; void add(int x, int lID) { l[lID].next = h[x]; h[x] = lID; } void dfs(int f, int now) { int k = h[now]; t[now].f[0] = f; t[now].d = t[f].d + 1; while(k) { if(l[k].p != f) { dfs(now, l[k].p); } k = l[k].next; } } void clcF() { for(int i = 1; i < DEEP; i++) { for(int j = 0; j <= n; j++) { t[j].f[i] = t[t[j].f[i - 1]].f[i - 1]; } } } void findLCA(int x, int y) { int tmp; int l; if(t[x].d > t[y].d) { tmp = x; x = y; y = tmp; } l = t[y].d - t[x].d; for (int i = DEEP - 1; i >= 0; i--) { if (l >= (1 << i)) { l -= (1 << i); y = t[y].f[i]; } } for(int i = DEEP - 1; i >= 0; i--) { if(t[x].f[i] != t[y].f[i]) { x = t[x].f[i]; y = t[y].f[i]; } } if(x == y) { printf("%d\n", x); }else { printf("%d\n", t[x].f[0]); } } int main() { int a, b; int x, y; scanf("%d %d %d", &n, &m, &s); for(int i = 1; i < n; i++) { scanf("%d %d", &a, &b); l[i* 2 - 1].p = b; add(a, i * 2 - 1); l[i* 2].p = a; add(b, i * 2); } t[0].f[0] = 0; t[0].d = 0; dfs(0, s); clcF(); for(int i = 1; i <= m; i++) { scanf("%d %d", &x, &y); findLCA(x, y); } return 0; }
原文:https://www.cnblogs.com/potatorain/p/9374826.html