题目链接:最近公共祖先(LCA)
题解
有很多种解法
法一:dfs序+ST算法
dfs序就是用深搜遍历整棵树得到的序列。
两个点的LCA一定是两个点在dfs序中出现位置之间的深度最小的点。
int deep[maxn<<1],pos[maxn<<1],cnt=0,seq[maxn<<1];
int dp[maxn<<1][20],n,m,s;;
vector<int> g[maxn];
void dfs(int u,int f,int step)
{
pos[u]=++cnt; seq[cnt]=u; deep[cnt]=step;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==f) continue;
dfs(v,u,step+1);
seq[++cnt]=u;
deep[cnt]=step;
}
}
void RMQ_init(int len)
{
for(int i=1;i<=len;i++) dp[i][0]=i;
for(int j=1;(1<<j)<=len;j++)
for(int i=1;i+(1<<j)-1<=len;i++)
{
if(deep[dp[i][j-1]]<deep[dp[i+(1<<(j-1))][j-1]])
dp[i][j]=dp[i][j-1];
else dp[i][j]=dp[i+(1<<(j-1))][j-1];
}
}
int rmq(int l,int r)
{
int k=log(r-l+1)/log(2);
return deep[dp[l][k]]<deep[dp[r-(1<<k)+1][k]] ? dp[l][k] : dp[r-(1<<k)+1][k];
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)
{
int u,v; scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(s,0,1);
RMQ_init(cnt);
while(m--)
{
int u1,u2;
scanf("%d%d",&u1,&u2);
printf("%d\n",seq[rmq(min(pos[u1],pos[u2]),max(pos[u1],pos[u2]))]);
}
}
法二:树链剖分