题目描述:

题目


解题思路:

首先思考暴力算法,我们先将待处理的 u,vu,vu,v 两点移动到相同深度的地方,然后两个一起向个自的父节点往上跳,直到跳到第一次父节点相同的时候这个相同的父节点即为它们的最近公共祖先。
很显然这个思想是没有问题,但是要是纯属暴力的一级一级往上跳这样实现,很显然会超时,那么我们就可以考虑倍增优化。

我们知道倍增可以把一大段繁琐的步骤压缩为一个一次就进行 2x2^x2x 次的步骤进行对步骤的加速。因此我们可以把算法中的一级一级往上跳转换为每次跳 2i2^i2i 级,也就是说在树上倍增找到最近公共祖先。
我们看看这棵树:

考虑寻找节点 17 和节点 18 的最近公共祖先。
我们发现 17 的深度为 666 ,而 181818 的深度为 777 ,因此我们要先将 181818 往上跳一级,变成 161616 ,这样子两点的深度就相同了,我们就可以开始寻找最近公共祖先。
由于深度为 6 ,因此第一次尝试倍增跳的级数为 2log262^{log_{~2}~6}2log 2​ 6 既向上跳 222^222 次。
然后我们发现跳到了节点 3 ,然后他们的父亲几点相同了,因此显然不能这么跳,于是我们减少跳的步数,考虑跳 212^121 步,这时 17跳到了 10,18 跳到了8,他们的父节点仍不相同,因此可以这么跳。
我们继续减少跳的步数,跳 202^020 级,他们分别跳到了7 和 5,他们的父节点相同了,也就是说7 和 5的是17 和 18的最近公共祖先。
这些跳的路径是这样的:
17−>10−>7−>317->10->7->317−>10−>7−>3
18−>16−>8−>5−>318->16->8->5->318−>16−>8−>5−>3

我们来考虑一些细节问题,为什么要跳到两个节点不相同但是父节点相同的节点,而不是直接跳到一个相同的节点呢?因为,如果一开始跳的步数就很大,那么可能会一条就跳到类似于根节点是多个节点祖先之类的节点,这时候显然根节点是两个节点的祖先没错,但是会存在这么一种情况,就算我们一开始跳到的就是两个节点的祖先,但是我们并不能保证这个祖先是离他俩最近的

因此我们要将步数递减,找到最后两个节点不同,但父节点相同的节点,那么这两个节点的父节点就肯定是是最近公共祖先。

正如上述步骤所说的,我们如果跳到了 2,那么2是17和18的祖先没错,但是我们无法保证它是最近的,但是我们如果跳到了 3和5,他们的父节点相同,那么可以证明3和5的父节点就是17和18的最近公共祖先。


对算法的再优化:

  • 对log的优化

我们发现第一次跳的步数总是2log2x2^{log_{~2}~x}2log 2​ x,那么这个 log2xlog_{~2}~xlog 2​ x 可以怎样快速求出来呢?

考虑建立一个递推式,设 lgilg_{i}lgi​ 表示 (log2i)+1(log_{~2}~i)+1(log 2​ i)+1 的值,那么:
lgi=lgi−1+(2lgi−1=i)lg_{i}=lg_{i-1}+(2^{lg_{~i-1}}=i) lgi​=lgi−1​+(2lg i−1​=i)
意思是,若 2lgi−12^{lg_{~i-1}}2lg i−1​ 等于 iii, 那么就加1,否则不加1,可以证明这个递推式可以正确求出所有 lgilg_ilgi​。

 for(int i=1;i<=n;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);
  • 对倍增的实现
    为了实现倍增,我们显然需要每一次都快速知道节点的第2x2^x2x 级祖先是谁:

设 fai,jfa_{i,j}fai,j​ 表示节点 iii 往上的第 2j2^j2j 级祖先,那么可以得到递推式:
fai,j=fafai,j−1,j−1fa_{i,j}=fa_{fa_{i,j-1},j-1} fai,j​=fafai,j−1​,j−1​

意思是 i 的第 2j2^j2j 级祖先为 i 的第 2j−12^{j-1}2j−1 级祖先的第 2j−12^{j-1}2j−1 级祖先,他是由 2x=2x−1+2x−12^x=2^{x-1}+2^{x-1}2x=2x−1+2x−1 来证明的。

 fa[s][0]=pre;for(int i=1;i<=lg[dep[s]];i++)fa[s][i]=fa[fa[s][i-1]][i-1];

这两条递推式是非常重要的,有了这两条递推式倍增的优化才能最大限度的发挥出来。


CODE:

完整的倍增 LCA 代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,root;
vector<int> g[500010];
int lg[500010];
int dep[500010],fa[500010][50];
void LG()
{for(int i=1;i<=n;i++)   //处理好lg数组lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
void dfs(int s,int pre)
{dep[s]=dep[pre]+1;  //在dfs中记录每个点的深度fa[s][0]=pre;for(int i=1;i<=lg[dep[s]];i++)fa[s][i]=fa[fa[s][i-1]][i-1];  //处理祖先for(int i=0;i<g[s].size();i++){if(g[s][i]==pre) continue;dfs(g[s][i],s);}
}
int lca(int u,int v)
{while(dep[u]>dep[v]) u=fa[u][lg[dep[u]-dep[v]]-1];  //若u的深度大于v的深度,那么利用倍增将u移到与v同级的地方while(dep[v]>dep[u]) v=fa[v][lg[dep[v]-dep[u]]-1];//若v的深度大于u的深度,那么利用倍增将v移动到与u同级的地方if(u==v) return u;  //如果u和v同点,那么显然他们互为最近公共祖先for(int i=lg[dep[u]]-1;i>=0;i--)if(fa[u][i]!=fa[v][i])  //若父节点不相同则像上跳,最终会跳到两个点不相同,但是父节点相同的两个点上{u=fa[u][i];v=fa[v][i];}return fa[u][0];  //返回最后相同的父节点
}
int main()
{cin>>n>>m>>root;int a,b;for(int i=1;i<n;i++){cin>>a>>b;g[a].push_back(b);g[b].push_back(a);}LG();dfs(root,0); for(int i=1;i<=m;i++){cin>>a>>b;cout<<lca(a,b)<<endl;}return 0;
}

最近公共祖先 (LCA倍增)相关推荐

  1. 最近公共祖先(LCA)/倍增LCA

    目录 题目描述: 分析: 链式前向星: 预处理lg数组: DFS求解深度depth和fa数组: LCA: 完整代码: 样例: 题目描述: 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. ...

  2. [模板]洛谷T3379 最近公共祖先(LCA) 倍增+邻接表

    一年前听说的这东西...现在终于会了... 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> ...

  3. 树上倍增法求最近公共祖先LCA

    LCA,最近公共祖先,这个东西有很多作用,因此,如何高效求出LCA就成了一个热点的讨论话题. 下面所有的讨论都以图中这棵树为例子. 先来了解下什么是倍增吧,倍增其实就是二分的逆向,二分是逐渐缩小范围, ...

  4. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)...

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  5. 【C++】最近公共祖先 LCA

    最近公共祖先 百科名片 简单引入 LCA的算法 暴力枚举法 Tarjan离线算法 倍增算法 例题: 题目描述 输入描述 输出描述 样例输入 样例输出 代码 百科名片 最近公共祖先 Lowest Com ...

  6. 图论--最近公共祖先LCA

    最近公共祖先LCA LCA(Least Common Ancestors),即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v最近的公共祖先(另一种说法,离树根最远的公共祖先) 最近公 ...

  7. 最近公共祖先 (LCA) [No. 21]

    问题: 给定一个二叉树,找到两个节点NA, NB的最近公共祖先(LCA). 比如对于下图,4 和 7 的 LCA 是6, 1和13的LCA 是 8. 我们这里先考虑一般的二叉树(BT),然后再考虑这个 ...

  8. FZU2207 以撒的结合(最近公共祖先lca,在线倍增)

    Problem Description 小茗同学最近在认真地准备比赛,所以经常玩以撒的结合. <以撒的结合>是一款由Edmund McMillen,Florian Himsl 开发,并由E ...

  9. 与图论的邂逅05:最近公共祖先LCA

    什么是LCA? 祖先链 对于一棵树T,若它的根节点是r,对于任意一个树上的节点x,从r走到x的路径是唯一的(显然),那么这条路径上的点都是并且只有这些点是x的祖先.这些点组成的链(或者说路径)就是x的 ...

最新文章

  1. latex 插图解释_大O符号-只需插图和视频即可解释
  2. POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)
  3. python主程序流程图_python中的图形渲染(流程图可视化)
  4. unity android eclipse,[转]Android笔记:Eclipse嵌入Unity3D开发的3D场景
  5. CornerEigenValsAndVecs函数
  6. java使用Executor(执行器)管理线程
  7. 如何在React Native中使用文本输入组件?
  8. mysql innodb隔离级别_浅析MySQL InnoDB的隔离级别
  9. C++工作笔记-根据父类指针获取子类类型(或不同的对象)
  10. 设置不显示用户名和主机名_谁说Excel中不可以有聚光灯效果:Excel高亮显示设置...
  11. html5 tooltips,纯js轻量级tooltips工具提示插件
  12. 点击按钮测试用例标题_如何才能设计出高质量的测试用例
  13. 自动驾驶 6-1: 横向车辆控制介绍 Lesson 1: Introduction to Lateral Vehicle Control
  14. 自学Java必备英语单词表
  15. 钢琴节奏时值测试软件,钢琴技巧:弹奏时值较长双音的技巧——自网络
  16. 魔幻!过年在家,Java和Python程序员比工资打起来了...
  17. 推荐几款好用的手机编程APP!
  18. 【Unity 题型】脚本开发
  19. ICLR 2021 NAS 相关论文(包含Workshop)
  20. HA集群强制进行Active/Standby切换的命令

热门文章

  1. 如何抵制人生中的诱惑
  2. Ubuntu下apt-get连接超时问题解决方法
  3. 「补课」进行时:设计模式(11)——游戏中的策略模式
  4. 不能打开Dropbox分享链接的问题
  5. 洛奇 服务器文件,《洛奇Mabinogi》官方网站
  6. html播放不了m3u文件,m3u-添加m3u文件的怎么不能播放 – 手机爱问
  7. 条码标签制作软件打印入场券抽奖券上的条码流水号
  8. 15---文件上传与下载
  9. 构造Linux的图形化安装程序(3)(转)
  10. AttributeError: ‘WebDriver‘ object has no attribute ‘find_element_by_id‘