基本思想就是先提到同一高度然后一起往上走,倍增只是把一步一步的走法变成二进制拆分
不过有要点
- 求f[x][i]数组
f[x][0]=father;
for (int i = 1; (1 << i) <= n; i++) {
f[x][i] = f[f[x][i - 1]][i - 1];
}
x的 2^i 祖先,就是x的 2^(i-1)祖先的 2^(i-1)个祖先。比如我的高祖父就是爷爷的爷爷
- 拉到同一高度
if(dep[a]!=dep[b]){
if (dep[a] < dep[b])
swap(a,b);
for( i=20;i>=0;i--){//提到同一高度
if(f[a][i]!=0&&dep[f[a][i]]>=dep[b])
a=f[a][i];
}
}
比如ab深度差(1111)二进制,a先跳到f[a][3],这时深度差就变成111了,以此类推
- 往上找祖先
for( i=20;i>=0;i--){
if(f[a][i]!=0&&f[b][i]!=0&&f[a][i]!=f[b][i])
{
a=f[a][i];
b=f[b][i];
}
}
return f[a][0];
这里没懂为啥返回f[a][0],后面琢磨明白了。
假设a与b找到lca还剩下(100)(二进制)的距离,那么理论上我们应该在i=2的时候跳一下,但是实操没跳,而是分别在i=1、i=0时跳了一下,这时候100就分解成011了,最后再返回一个f[a][0],就相当于又走了一次 i=0,凑成了100
AC代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(3)
#define endl "\n"
#define four(i, a, b) for(int i=a;i<=b;i++)
#define fourr(i, a, b) for(int i=a;i>=b;i--)
#define Max(a, b) (a>b?a:b)
#define Min(a, b) (a<b?a:b)
#define ll long long
#define inf 0x3f3f3f3f
#define mem(a, b) memset(a,b,sizeof(a))
#define lowbit(x) (x&(-x))
const ll maxn = 5e5 + 5;
const ll maxm = 1e5 + 5;
const ll mod = 233333;
int n, m;
int s;
vector<int> v[maxn];
int f[maxn][21];
int dep[maxn];
bool vis[maxn];
int lca(int a,int b){
int i;
if(dep[a]!=dep[b]){
if (dep[a] < dep[b])
swap(a,b);
for( i=20;i>=0;i--){//提到同一高度
if(f[a][i]!=0&&dep[f[a][i]]>=dep[b])
a=f[a][i];
}
}
if(a==b)return a;
for( i=20;i>=0;i--){
if(f[a][i]!=0&&f[b][i]!=0&&f[a][i]!=f[b][i])
{
a=f[a][i];
b=f[b][i];
}
}
return f[a][0];
}
void dfs(int x, int father) {
if (vis[x])return;
vis[x]=1;
dep[x] = dep[father] + 1;
f[x][0] = father;
for (int i = 1; (1 << i) <= n; i++) {
f[x][i] = f[f[x][i - 1]][i - 1];
}
for (const auto &item: v[x]){
dfs(item,x);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> s;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
dfs(s, 0);
for (int i = 0; i < m; ++i){
int a,b;
cin >> a >> b;
cout<<lca(a,b)<<endl;
}
}