LCA(Least Common Ancestors), 即最近公共祖先, 是指这样一个问题: 在有根树中, 找出某两个结点 u 和 v 最近的公共祖先 (另一种说法, 离树根最远的公共祖先).
知识需求: 1)RMQ 的 ST 算法 2) 欧拉序列
1)RMQ 的 ST 算法:
可以参考我的这篇博客: RMQ 原理及实现
2) 欧拉序列:
所谓欧拉序, 就是从根结点出发, 按 dfs 的顺序经过每一个结点最后绕回原点的顺序, 比如下面这个例子, 欧拉序就是 A-B-D-B-E-G-E-B-A-C-F-H-F-C-A
那么欧拉序和 rmq 与 LCA 有什么关系呢, 首先我们知道 RMQ 可以方便的在线求出区间最小值, 以求上图中 DG 两点最近公共祖先为例, 我们先处理出他的欧拉序, 我们记录下每个结点第一次被访问的时间, 以及每个时间访问的结点编号与结点深度, 这时, 我们不难发现, D 与 G 第一次出现的时间之间的区域深度最小值就是这两个点对应的最近公共祖先 B 的深度, 我们修改 rmq, 让其不再返回最小深度, 而是返回区间最小深度对应的下标, 这里就是求欧拉序中的访问时间, 有了这个时间, 加上之前的记录, 我们可以直接得出该点的编号, 从而求出最近公共祖先.
练习题: 题目链接 https://cn.vjudge.NET/problem/Gym-101002I
练习题 AC 代码:
- #include <iostream>
- #include <cstdio>
- #include <algorithm>
- #include <cstring>
- #include <vector>
- #include <set>
- #include <queue>
- #include <map>
- using namespace std;
- const int MAXN = 200010 * 2;
- int rmq[MAXN * 2]; // 节点深度序列
- struct ST {
- int mm[MAXN * 2];
- int dp[MAXN][30];
- void init(int n) {
- mm[0] = -1;
- for(int i = 1; i <= n; ++i) {
- mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
- dp[i][0] = i;
- }
- for(int j = 1; j <= mm[n]; ++j)
- for(int i = 1; i + (1 <<j) - 1 <= n; ++i)
- dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
- }
- int query(int a, int b) {
- if(a> b) swap(a, b);
- int k = mm[b - a + 1];
- return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k];
- }
- };
- struct Edge{
- int to, next;
- };
- Edge edge[MAXN * 2];
- int tot, head[MAXN];
- int F[MAXN * 2];
- int P[MAXN];
- int cnt;
- ST st;
- void init() {
- tot = 0;
- memset(head, -1, sizeof(head));
- }
- void addedge(int u, int v) {
- edge[tot].to = v;
- edge[tot].next = head[u];
- head[u] = tot++;
- }
- int d[MAXN];
- void dfs(int u, int pre, int dep) {
- d[u] = dep;
- F[++cnt] = u;
- rmq[cnt] = dep;
- P[u] = cnt;
- for(int i = head[u]; i != -1; i = edge[i].next) {
- int v = edge[i].to;
- if(v == pre) continue;
- dfs(v, u, dep + 1);
- F[++cnt] = u;
- rmq[cnt] = dep;
- }
- }
- void LCA_init(int root, int node_num) {
- cnt = 0;
- dfs(root, root, 0);
- st.init(2 * node_num - 1);
- }
- int query_lca(int u, int v) {
- return F[st.query(P[u], P[v])];
- }
- int main()
- {
- int T, N, u, v;
- scanf("%d", &N);
- init();
- for(int i = 1; i < N; ++i) {
- scanf("%d%d", &u, &v);
- addedge(u, v);
- addedge(v, u);
- }
- LCA_init(1, N);
- long long ans = 0;
- for(int i = 1; i + i <= N; ++i) {
- for(int j = i + i; j <= N; j += i) {
- ans += d[i] + d[j] + 1 - 2 * d[query_lca(i, j)];
- }
- }
- printf("%lld\n", ans);
- return 0;
- }
来源: https://www.cnblogs.com/fan-jiaming/p/9741368.html