注意: \color{red}{注意:} 注意: 个人博客传送门
图论(Graph Theory)
LCA (最近公共祖先)
查找最近公共祖先的方法:
-
向上标记法 O ( n ) O(n) O(n)
-
倍增法 O ( n l o g n ) O(nlogn) O(nlogn)
- f a [ i , j ] fa[i, j] fa[i,j] 表示从 i i i 开始,向上走 2 j 2^j 2j 步所能到达的节点, 0 ≤ j ≤ l o g ( n ) 0 \le j \le log(n) 0≤j≤log(n)
- d e p t h [ i ] depth[i] depth[i] 表示深度
步骤:
- 先将两个点跳到同一层。
- 让两个点同时向上跳,直到跳到他们的最近公共祖先的下一层。
预处理 O ( n l o g n ) O(nlogn) O(nlogn)
查询 O ( l o g n ) O(logn) O(logn)
-
T a r j a n Tarjan Tarjan 离线求 L C A LCA LCA O ( n + m ) O(n+m) O(n+m)
练习
祖孙询问
算法:倍增法求 l c a lca lca
#include <bits/stdc++.h>
using i64 = int64_t;
constexpr int N = 4E4 + 10;
int f[N][20], dep[N];
int lca(int x, int y) {
if (dep[x] < dep[y]) {
std::swap(x, y);
}
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) {
x = f[x][i];
}
}
if (x == y) return x;
for (int i = 19; i >= 0 && y != x; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
int rt = -1;
std::vector<std::vector<int>> adj(N);
for (int i = 0; i < n; i++) {
int u, v;
std::cin >> u >> v;
if (v == -1) {
rt = u;
continue ;
}
adj[u].push_back(v);
adj[v].push_back(u);
}
auto dfs = [&](auto self, int u, int fa) -> void {
f[u][0] = fa;
dep[u] = dep[f[u][0]] + 1;
for (int i = 1; i < 20; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (auto x : adj[u]) {
if (x == fa) continue ;
self(self, x, u);
}
};
dfs(dfs, rt, 0);
int q;
std::cin >> q;
while (q--) {
int x, y;
std::cin >> x >> y;
int ans = 0;
if (lca(x, y) == x) {
ans = 1;
} else if (lca(x, y) == y) {
ans = 2;
}
std::cout << ans << "\n";
}
return 0;
}