一.概述
二.方法
1.树上的路径:
•众所周知,两点在树上的路径是唯一的,但是如何快速维护路径的相关信息(路径权值和、路径边权最大/最小值等)?
•在解决这些问题之前,首先考虑一个问题:如何快速找到两点在树上的路径?
•假设两个节点为x和y,若x是y的祖先或y是x的祖先,我们只需直接从深度较大的节点向上访问即可找到路径。
•若没有这种祖先关系,则我们只要找到x和y的最近公共祖先,将两个节点到他们最近公共祖先的路径连接起来即可。
2.稀疏表与祖先:
•与稀疏表类似(不知道稀疏表的自己查),我们不必保存每个节点的所有祖先信息,而是只保存其所有2^k倍祖先(倍增),换句话说,用fa[x][k]表示x的2^k倍祖先。
•fa[x][0]:x的1倍祖先,即x的父节点。
•fa[x][1]:x的2倍祖先,即x父节点的父节点
• fa[x][2]:x的4倍祖先,即x父节点的父节点的父节点的父节点。
• ………………
• 更新操作也很简单:fa[x][k+1]=fa[fa[x][k]][k]。
3.祖先的查询:
• 如何查询一个节点的k倍祖先?
• 方法:每次找到k的二进制表示中,从低到高第一个1所在的位置,假设是第x位,则先访问该节点的第2x倍祖先,以此类推
• 可能会用到lowbit(不知道的自己查!)lowbit(x)=x&(-x);
4.最近公共祖先:
5.最近公共祖先的实现
• 遵循以下步骤来找到两个节点x和y的最近公共祖先:
• ①不妨设x和y的深度分别为dep[x]和dep[y],且dep[x]<dep[y],则我们首先找到y的
dep[y]-dep[x]倍祖先,并将y移动到该位置,更新y的值,若此时y与x已经相等,则表
明x就是两者的最近公共祖先,不再执行下面的过程
• ②取k为满足下述条件的最大非负数:x和y的2^k倍祖先不相等,然后将x和y都移动到
其2^k倍祖先的位置并更新他们的值,显然k可以枚举也可以二分。
• ③假设不存在满足条件的k,则说明此时x和y的父节点一定是同一个点,而这个点就
是两点的最近公共祖先。
三.例题
1.模板
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, m, root, cnt;
int depth[500010], head[500010], fa[500010][20];
bool vis[500010];
struct data
{
int to, next;
}edge[500010 << 1];
inline void addedge(int u, int v)
{
edge[++cnt].to = v; edge[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int x)
{
vis[x] = true;
for(int i = 1; i <= 18; ++i) {
if(depth[x] < (1 << i)) break;
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
for(int i = head[x]; i; i = edge[i].next) {
if(vis[edge[i].to]) continue;
depth[edge[i].to] = depth[x] + 1;
fa[edge[i].to][0] = x;
dfs(edge[i].to);
}
}
inline int lca(int x, int y)
{
if(depth[x] < depth[y]) swap(x, y);
int d = depth[x] - depth[y];
for(int i = 0; i <= 18; ++i)
if((1 << i) & d) x = fa[x][i];
for(int i = 18; i >= 0; --i)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
if(x == y) return x;
else return fa[x][0];
}
int main() {
n = read(), m = read(), root = read();
for(int i = 1, u, v; i < n; ++i)
{
u = read(), v = read();
addedge(u, v);
addedge(v, u);
}
dfs(root);
for(int i = 1, x, y; i <= m; ++i)
{
x = read(), y = read();
printf("%d\n", lca(x, y));
}
return 0;
}