倍增lca,用二进制优化,在寻找最长公共祖先时,进行寻找,是一种在线查找做法
需要的额外数组有fij di
fij 表示从i出发的第2^j级祖先
di是与根节点的距离
核心转移方程
预处理f[i,j]=f[f[i][j-1]][j-1]
也就是说i的第2^j级祖先等于从i的2^(j-1)级祖先的2^(j-1)级祖先
2^j=2^(j-1)+2^(j-1)
如何查询
对于两个节点x,y
先不妨假设x>y
首先是自动至同一级,也就是将x向他的祖先上自动,根据d[i]数组,当d[f[x][j]]>=d[y]时就进行转移
x=f[x][j]
(对于j的枚举似乎是可以使用预处理log2进行优化,但是由于这个j枚举的是指数级别的,也就是从20左右向下枚举,所以一般作用应该不大)
注意是从大往小枚举11=8+2+1
从小到大会变成1+2+4+?
如果转移后发现x=y,那说明y就是公共祖先,直接返回就可以了
否则,对x,y进行同时的转移,直到f[x][j]=f[y][j](x,y的j级祖先是相同的)
此时,返回f[x][0]即为最近公共祖先
//倍增LCA
#include<bits/stdc++.h>
using namespace std;
const int N = 500010,M = N*2;
int h[N],e[M],ne[M],cnt;
int dist[N];//深度
int f[N][20];//二进制优化 f<i,j>表示从i向上走2^j能到达的点
void add(int a,int b){
e[cnt] = b,ne[cnt] = h[a],h[a] = cnt++;
}
int n,q,s;
void bfs(int u){//bfs预处理
memset(dist,0x3f,sizeof dist);
dist[0] = 0;
dist[u] = 1;
queue<int>q;
q.push(u);
while(q.size()){
int x = q.front();q.pop();
for(int i = h[x];~i; i = ne[i]){
int j = e[i];
if(dist[j] > dist[x] + 1){
dist[j] = dist[x] + 1;
q.push(j);
f[j][0] = x;//j向上走1步是x
for(int k = 1; k <= 19; k++){
f[j][k] = f[f[j][k - 1]][k - 1];//关键转移
}
}
}
}
}
int lca(int x,int y){
if(dist[x] < dist[y])swap(x,y);
for(int k = 19; k >= 0; k--){
if(dist[f[x][k]] >= dist[y]){
x = f[x][k];
}
}//使y和x在同一级
if(x == y)return x;//需要加
for(int k = 19; k >= 0; k--){
if(f[x][k] != f[y][k]){
x = f[x][k];
y = f[y][k];
}
}
return f[x][0];
}
int main(){
//初始化
memset(h,-1,sizeof h);
scanf("%d%d%d",&n,&q,&s);
for(int i = 1; i < n; i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
bfs(s);
while(q--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}