LCA之倍增法
最近公共祖先简称 LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。
除去朴素算法,LCA还可以用倍增法来求,倍增法在算法中的应用还是比较多的,可以用来求ST表(区间最大最小值),还可以用来求LCA。其中求LCA的方法如下:
整体流程
预处理
用BFS或者DFS处理
- deg[i]:结点i的深度
- fa[i][j]:每个结点往上跳2^j后的位置
- tu,tv为跳到的位置
计算
假设求u和v的LCA,核心就只有两步
- 将u和v先跳到同一层
- u和v同时往上跳
- 跳的时候总是跳2 ^ j个,从大到小跳(判断能不能跳,能跳就跳)
- 最后返回fa[tu][0]
原理
我们假设u跳到LCA的距离为Len,因为Len可以用二进制表示,也就是说Len可以拆分为很多个2的倍数,所以我们每次跳2的j次个一定可以跳到LCA,同时我们跳的次数的复杂度就是logn的复杂度。
关于判断我们选择的j次是不是要跳的,我们可以判断跳了2的j次以后所在的位置,如果他俩相等说明跳多了,因为我们最后的结果是跳到LCA的下一个,所以他们一定不会相等,相等即说明跳多了。
看一下模板里面的具体实现可以更加清楚一点。
模板
copy from kuangbin
const int MAXN=10010;
// 跳转的大小,可以手动调大
const int DEG=20;
// 链式前向星部分
struct Edge{
int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
void addedge(int u,int v) {
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void init() {
tot=0;
memset(head,-1,sizeof(head));
}
// fa[i][j]表示结点i的第2^j个祖先
int fa[MAXN][DEG];
// 深度数组
int deg[MAXN];
// 标记深度,预处理fa数组
void BFS(int root) {
queue<int>que;
deg[root]=0;
fa[root][0]=root;
que.push(root);
while(!que.empty()) {
int tmp=que.front();
que.pop();
for (int i=1;i<DEG;i++)
fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
for (int i