当前位置: 首页 > news >正文

洛谷P3379 【模板】最近公共祖先(LCA)

输入格式

第一行包含三个正整数 �,�,�,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 �−1 行每行包含两个正整数 �,�,表示 � 结点和 � 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 � 行每行包含两个正整数 �,�,表示询问 � 结点和 � 结点的最近公共祖先。

输出格式

输出包含 � 行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例 #1

输入 #1

5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5

输出 #1

4 4 1 4 4

说明/提示

对于 30% 的数据,�≤10,�≤10。

对于 70% 的数据,�≤10000,�≤10000。

对于 100% 的数据,1≤�,�≤5×105,1≤�,�,�,�≤�,不保证�≠�。

样例说明:

该树结构如下:

第一次询问:2,4 的最近公共祖先,故为 4。

第二次询问:3,2 的最近公共祖先,故为 4。

第三次询问:3,5 的最近公共祖先,故为 1。

第四次询问:1,2 的最近公共祖先,故为 4。

第五次询问:4,5 的最近公共祖先,故为 4。

故输出依次为 4,4,1,4,4。

2021/10/4 数据更新 @fstqwq:应要求加了两组数据卡掉了暴力跳。


这题是最近公共祖先(LCA)的标准模板题
我们可以预处理出两点上跳的行为
定义dp[i][j]:从节点i出发走2^j步能到达的节点
那么类似于ST表
{
lastpos=dp[i][j-1]
dp[i][j]=dp[lastpos][j-1]
}
就是先走2^(j-1)步走到lastpos
再从lastpos走2^(j-1)步到达节点dp[lastpos][j-1]
总步数2(j-1)+2(j-1)=2^j
起点i,终点dp[lastpos][j-1]=dp[i][j]
所以递推式就是:
dp[i][j]=dp[dp[i][j-1][j-1]
还有一点就是我们用dp[i][0]记录i的父亲节点
这些都可以用dfs来解决:

void dfs(int pos,int fa){ deep[pos]=deep[fa]+1; dp[pos][0]=fa; for(auto v:g[pos]){ if(v==fa) continue; dfs(v,pos); } } -----

再者就是LCA函数
先传入两个节点x,y
确保deepx<deep[y]
那么显然的要求y节点先跳上与x节点等高的位置:

int d=deep[y]-deep[x]; for(int k=0;k<=20;k++){ if(d>>k&1) y=dp[y][k]; }

如果x==y,说明两者的LCA就是x!!!!

if(x==y) return x;

如果x!=y:
那就一起往上跳
注意!要从最大的步数开始跳,因为从最小步数开始跳的话,一旦答案再很高的位置就会浪费很多时间复杂度!

for(int k=20;k>=0;k--){ if(dp[x][k]!=dp[y][k]) //如果发现跳了这么多步还没有公共的节点 x=dp[x][k],y=dp[y][k]; //直接跳,就不用再中间的节点了,省时省力! }

跳到最后,x,y都离最近公共祖先差一个位置,直接c++ return dp[x][0](或者c++ return dp[y][0]一样的)


ACcode:

#include<bits/stdc++.h> using namespace std; const int N=5e5+5; int n,m,s; int dp[N][25],deep[N]; //father=dp[i][0]; vector<int> g[N]; /* dp[i][j]:从节点i往上跳2^j步,达到的节点编号 lastpos=dp[i][j-1] 先从i跳2^(j-1)步 dp[i][j]=dp[lastpos][j-1] 再从lastpos跳2^(j-1)步 所以:dp[i][j]=dp[dp[i][j-1]][j-1]; */ void dfs(int pos,int fa){ deep[pos]=deep[fa]+1; dp[pos][0]=fa; for(auto v:g[pos]){ if(v==fa) continue; dfs(v,pos); } } int LCA(int x,int y){ if(deep[x]>deep[y]) swap(x,y); int d=deep[y]-deep[x]; for(int k=0;k<=20;k++) if(d>>k&1) y=dp[y][k]; if(x==y) return x; //倒序先大步跳 for(int k=20;k>=0;k--) if(dp[x][k]!=dp[y][k]) x=dp[x][k],y=dp[y][k]; return dp[x][0]; } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>m>>s; for(int i=1;i<=n-1;i++){ int x,y; cin>>x>>y; g[x].push_back(y); g[y].push_back(x); } dfs(s,s); for(int j=1;j<=20;j++) for(int i=1;i<=n;i++) dp[i][j]=dp[dp[i][j-1]][j-1]; while(m--){ int x,y; cin>>x>>y; cout<<LCA(x,y)<<endl; } return 0;
http://www.jsqmd.com/news/1113289/

相关文章:

  • 机器学习模型生产化部署:从Notebook到高可用服务的实战路径
  • 【功能开发】添加按月按日查询器,禁用当月当天之后的选择
  • 2026年7月更新 | 关键词:企业AI落地避坑指南 · AI服务商怎么选 · PDCA陪跑
  • 如何在通达信中实现智能缠论自动化分析:ChanlunX插件完整指南
  • 云克隆 Luminex 多因子技术在细胞因子领域是应用
  • 5分钟打造智能媒体库:MetaTube插件为Jellyfin/Emby提供完整元数据解决方案
  • 手机木马取证实战:从安装源定位到行为特征分析的完整指南
  • MySQL 自动安装Python脚本操作手册
  • Meta 掀翻桌子进军云计算!“Meta Compute”曝光:AI 拼的不是模型,而是算力所有权
  • 5G基站与终端射频验收——思仪这套仪器组合为什么成了主流
  • DigitalOcean 推出大模型自动化评测功能,上线前精准避坑
  • 基于STM32的智能手环设计与实现
  • AI信息过载时代的信息筛选与落地实践指南
  • 2026青岛AI数字人公司排行榜:本地服务商技术实力与落地能力盘点
  • SOHOTHEME外贸SOHO独立站WordPress主题
  • 强化学习入门:从回报、价值函数到贝尔曼方程的工程化理解
  • 【计算机Java毕业设计案例】基于 SpringBoot 的高校学生组织资源资料整合系统的设计与实现 基于 SpringBoot 的校园学生活动策划与落地管理系统(程序+文档+讲解+定制)
  • Hermes-Agent :Windows 环境完整安装与 API 中转配置
  • CSRF攻击原理与防御实战:从DVWA靶场到企业级防护方案
  • 【Java课程设计/毕业设计】基于 SpringBoot 的 “图书森林” 馆藏图书智能借阅系统的设计与实现 基于 SpringBoot 的共享图书资源可视化管理系统【附源码、数据库、万字文档】
  • 深度强化学习算法实战:从Q-Learning到PPO的工程落地指南
  • CNC加工厂为什么总是延期?从订单跟踪、生产进度到排产管理看问题根源
  • 小程序商城哪个平台好?适合零售、餐饮和服务商家的选型逻辑
  • ClaudeCode模型选型指南:如何为真实编码场景匹配最优AI模型
  • Oracle与Java安全实战:从SQL注入防御到TDE加密的纵深防护体系
  • LINUX编译地图软件GDAL
  • GB_T_27930_报文大全
  • A类系统车桩充电通信流程
  • 携程酒店详情信息一键获取,item_get_appAPI接口讲解
  • Virbox Protector 从何而来:深盾科技的软件保护演进