前言
倍增法主要分为三个步骤:
- 读入图后,进行遍历,获得deep[]深度数组和fa[i][0]父节点数组
- 进行dp/状态转移获得 f a [ i ] [ 2 j ] fa[i][2^j] fa[i][2j],即第 2 j 2^j 2j个祖先
- 对于待查询点u, v,先让它们到达统一深度,再去循环访问它们的第 2 j 2^j 2j个祖先
至此,便完成了整个查询过程,时间复杂度是 O ( n log n ) + q O ( log n ) O(n \log n)+qO(\log n) O(nlogn)+qO(logn),q是查询次数
一、例题 p3379
二、思路及代码
1.思路
纯模板题,直接套板子
2.代码
代码如下:
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 500005;
struct e {
int to, next;
} edge[maxn << 1];
int head[maxn], cnt;
int n, m, root;
int deep[maxn], f[maxn][21]; // f[i][j]: i的第2^j层的祖先
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
for (int i = 0; i < maxn; i++) edge[i].next = -1;
}
void addedge(int u, int v) {
edge[cnt] = e{v, head[u]};
head[u] = cnt++;
}
void dfs(int u, int fa) {
deep[u] = deep[fa] + 1;
f[u][0] = fa;
for (int i = head[u]; i != -1; i = edge[i].next) {
if (edge[i].to == fa) continue;
dfs(edge[i].to, u);
}
}
void LCApre() {
deep[0] = 0;
dfs(root, 0);
for (int i = 1; i <= 20; i++) // f[][0]为递归基
for (int u = 1; u <= n; u++) f[u][i] = f[f[u][i - 1]][i - 1];
}
int LCA(int u, int v) {
if (deep[u] < deep[v]) swap(u, v); //保证deep[u] > deep[v]
for (int i = 20; i >= 0; i--) {
if (deep[f[u][i]] >= deep[v]) u = f[u][i];
} // 此时 deep[u] = deep[v]
if (u == v) return u;
for (int i = 20; i >= 0; i--) {
if (f[u][i] != f[v][i]) {
u = f[u][i];
v = f[v][i];
}
} // 此时 f[u][i] = f[v][i]
return f[u][0];
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
init();
int u, v;
scanf("%d%d%d", &n, &m, &root);
for (int i = 0; i < n - 1; ++i) {
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
LCApre();
for (int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
printf("%d\n", LCA(u, v));
}
return 0;
}