题目链接:
代码:
细节解释:
1.思想上,用到了倍增的思想,让寻找LCA变得更快。
2.注意 f 数组的定义,还有lg初始化的方式
3.最重要的一句代码:2^(i-1)级祖先的2^(n-1)祖先,就是当前的2^n级祖先(因为2^i = 2^(i-1) + 2^(i-1))
f[u][i] = f[f[u][i-1]][i-1];
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500005;
int f[maxn][20]; //f[i][j]表示i的第2^j级祖先
int d[maxn]; //结点深度
int lg[maxn]; //预处理以二为底的对数
int n, m, s;
vector<int> g[maxn];
void dfs(int u, int fa){
f[u][0] = fa, d[u] = d[fa]+1; //直接父节点,深度
for(int i=1; i<=lg[d[u]]; i++) //记录倍增级数的祖先编号
f[u][i] = f[f[u][i-1]][i-1];//重点:意思是now的2^i祖先等于now的2^(i-1)祖先的2^(i-1)祖先
//2^i = 2^(i-1) + 2^(i-1)
for(int v : g[u])
if(v != fa) dfs(v, u);
}
int LCA(int x, int y){
if(d[x] < d[y]) swap(x, y); //不妨假设x的深度 >= y的深度,即x位置比y低
while(d[x] > d[y]) x = f[x][lg[d[x]-d[y]]]; //x位置倍增上升,直到和y齐平
//由于lg是向下取整,这里会根据倍增级数逐步接近y的高度
if(x == y) return y; //特殊情况,y正好是x的祖先
for(int k=lg[d[x]]; k>=0; k--){
//这步也是重点,倍增上升,直到刚好找到LCA的下面两个子节点,后面就不会再进入if语句中了
if(f[x][k] != f[y][k]) {x=f[x][k]; y=f[y][k];}
}
return f[x][0];
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> s;
for(int i=1; i<=n-1; i++){
int a, b; cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
for(int i=2; i<=n; i++) lg[i] = lg[i>>1]+1; //lg数组预处理,和st数组模板中的一样
dfs(s, 0);
for(int i=1; i<=m; i++){
int a, b; cin >> a >> b;
cout << LCA(a, b) << '\n';
}
}