#F. 最も近い共通祖先

トピック


一連の考え

詳細については、「説明された最も近い共通の祖先」を参照してください。


コード

#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;
}

おすすめ

転載: blog.csdn.net/weq2011/article/details/128767363