トピック
一連の考え
詳細については、「説明された最も近い共通の祖先」を参照してください。
コード
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,u,v;
/*
pre[j]:对于第i条边来说,它的上一条边是哪一条边
now[x]:对于点x来说,最后一条描述它充当父结点的边是哪一条边
son[i]:在第i条边中,充当子结点的点是哪一个
*/
int eg,pre[100001],now[100001],son[100001],deep[100001],mx[1000001][70];
void adeg(int u,int v)
{
pre[++eg] = now[u];
now[u] = eg;
son[eg] = v;
}
void dfs(int u,int fa,int dep)//计算每个点的深度
{
deep[u] = dep;//节点u的深度为dep
for(int i = now[u]; i; i = pre[i])
{
int v = son[i];
if(v == fa) continue;
mx[v][0] = u;//节点v向上跳2^0个位置后节点为u(因为v的父节点就是u)
dfs(v,u,dep + 1);
}
}
int lca(int u,int v)
{
if(deep[u] < deep[v]) swap(u,v);//保证u为u,v中较深的节点
for(int i = 60; i >= 0; i--)
if(deep[mx[u][i]] >= deep[v])
u = mx[u][i];//将u跳到和v同一层
if(u == v) return u;
for(int i = 60; i >= 0; i--)//每次u,v同跳2^i
if(mx[u][i] != mx[v][i])//跳后不能相等,相等的化是公共祖先,但不一定最近
{
u = mx[u][i];
v = mx[v][i];
}
return mx[u][0];//u,v都跳到最近公共祖先的下一层,这样再跳一步就是最近公共祖先了
}
signed main()
{
scanf("%lld%lld",&n,&q);
for(int i = 1; i < n; i++)
{
cin>>u>>v;
adeg(u,v);
adeg(v,u);
}
dfs(1,0,1);
for(int j = 1; j <= 60; j++)
for(int i = 1; i <= n; i++)
mx[i][j] = mx[mx[i][j - 1]][j - 1];
while(q--)
{
scanf("%lld%lld",&u,&v);
printf("%lld\n",lca(u,v));
}
return 0;
}