倍增算法可以在线求树上两个点的LCA,预处理时间复杂度为 O(n log n),查询复杂度O (log n)
倍增算法需要一个预处理过程,在dfs过程中求出每个节点v的深度deep[v] 和该节点上2^i的祖先节点f[v][i]
求最近公共祖先,根据两个节点的的深度,如不同,向上调整深度大的节点,使得两个节点在同一层上,如果正好是祖先结束,否则,将连个节点同时上移,查询最近公共祖先。
下面是预处理过程
[cc lang=”C++”]
void dfs(int index){
f[index][0] = fa[index]; //index节点上1个祖先节点就是index的父亲
for(int i=1;i<=MAXM;i++)
f[index][i] = f[f[index][i-1]][i-1]; //倍增的体现
for(int i=0;i<map[index].size();i++){
int v = map[index][i];
if(v != fa[index]){
fa[v] = index; //记录父亲
deep[v] = deep[index] +1 ; //记录深度
dfs(v);
}
}
}
[/cc]
LCA过程
[cc lang=”C++”]
int lca(int x,int y){
if(deep[x] < deep[y])
swap(x,y); //x为更深的节点
for(int i=MAXM;i>=0;i--){
if(deep[y] <= deep[f[x][i]]){ //注意等号(还未向上跳)
x = f[x][i]; //使x和y在同一深度
}
}
if(x==y)
return x; //y是x的祖先
for(int i=MAXM;i>=0;i--){
if(f[x][i] != f[y][i]){
//一起向上跳,想象需要跳i下,那么i可以转化为二进制数字
x = f[x][i];
y = f[y][i];
//不要break了
}
}
return f[x][0]; //此时他们父亲节点相等,所以返回父亲节点(判断语句)
}
[/cc]
MAXM = log(MAXN)
另外注意要将dfs的根节点的父亲赋值为根节点,否则若公共祖先为根节点的时候,会输出0
参考资料
sllr15 – 最近公共祖先