蓝桥杯-最近公共祖先

文章讲述了如何在给定的树形结构图中,利用深度优先搜索和最近公共祖先(LCA)算法来找出任意两点间的最近公共祖先。通过构建深度数组和路径压缩数组,有效地解决了图论中的查询问题。
摘要由CSDN通过智能技术生成
"""
https://www.lanqiao.cn/problems/4385/learning/?page=1&first_category_id=1&name=%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88
"""
import sys
sys.setrecursionlimit(10000)
input = sys.stdin.readline

# 预处理deep数组和p数组
def dfs(u, fa):
  deep[u] = deep[fa] + 1
  p[u][0] = fa
  for i in range(1, 21):
    # 节点u往上走2^i步 = 节点u网上走2^(i-1)步所在的节点往上走2^(i-1)步
    p[u][i] = p[p[u][i - 1]][i - 1] 
  for v in G[u]:
    if v == fa: continue
    dfs(v, u)

def lca(x, y):
  # 保证x更深
  if deep[x] < deep[y]:
    x, y = y, x
    # x利用倍增的方法往上走, 使得deep[x] == deep[y]
    # i 从大往小枚举 走2^i步
    # 如果走2^i到达的点p[x][i]的深度仍大于deep[y]则可以走
  for i in range(20, -1, -1):
    if deep[p[x][i]] >= deep[y]:
      x = p[x][i]

  # 此时deep[x] == deep[y]
  if x == y:
    return x
  
  # 一起往上走, 如果走2^i步, 两个点的公共祖先相同则不可以走, 否则可以走,
  # 因为i从大往小, 走2^i步肯定是它们的公共祖先, 但不是最近的, 所以需要
  # 不断走, 让x和y走到最近公共祖先下的两个儿子节点, 然后再走一步即是最近
  # 公共祖先
  for i in range(20, -1, -1):
    if p[x][i] != p[y][i]:
      x, y = p[x][i], p[y][i]
  return p[x][0]

n = int(input())
G = [[] for i in range(n + 1)]
# deep[u]表示节点u的深度
deep = [0] * (n + 1)
# p[u][i]表示节点u往上走2^i到达的节点,
# 初始值默认都是到达根节点, 因为根节点深度最小为0, 走2^i步后没有该节点也不影响结果
p = [[0] * 21 for i in range(n + 1)]

# 邻接表存图
for _ in range(n - 1):
  u, v = map(int, input().split())
  G[u].append(v)
  G[v].append(u)

dfs(1, 0)
Q = int(input())
for _ in range(Q):
  x, y = map(int, input().split())
  print(lca(x, y))

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好无聊啊,烦死

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值