【模板时间】◆模板·I◆ 倍增计算LCA
【模板·I】LCA(倍增版)
既然是一篇重点在于介绍、分析一个模板的Blog,作者将主要分析其原理,可能会比较无趣……(提供C++模板)
另外,给reader们介绍另外一篇非常不错的Blog(我就是从那篇博客开始自学LCA的):+LCA-by 殇雪+
一、原理
LCA即最近公共祖先,一般用LCA(u,v)表示u、v的最近公共祖先。举个例子:
(Tab:以下“树”均指有根树)由于在树中,除根节点的每个节点都有且仅有一个父节点,我们很容易得到一个结论——u,v的最近公共祖先的任意祖先一定也是u,v的公共祖先。假设x是u,v的公共祖先,LCA(u,v)≤x。倍增求LCA的基础是 2进制能够表示任意整数 。所以令u,v的最近公共祖先与u、v分别距离lu、lv,并可以将其表示成2进制。
设 dfu[v][k] 表示 节点v向上寻找到的第2k代祖先(比如之前的图中,dfu[4][1]=1),dep[v] 表示 v的深度(根节点的深度依照题目定为1或0)。
不妨设 dep[v]>dep[u] 。首先要使u、v同层,即dep[u]=dep[v],可以通过将v上移到它的 dep[u]-dep[v] 代祖先来实现,再将dep[v]-dep[u]转为2进制,就可以通过dfu实现。
u、v同层后,设它们距离最近公共祖先l个单位。同样,l也可以表示为2进制,也就可以通过dfu解决。
由于每一次移动都是在2进制下进行,求LCA的复杂度大约可看为 O(log2n)。
二、算法实现
①大致步骤:
初始化: DFS初始化每个点v的dep[v]以及直接父亲(dfu[v][0]);
递推计算全部dfu;
计算LCA: 上移u、v至同一层;
同时上移u、v找最近公共祖先;
②初始化:
DFS可以通过参数下传父亲节点以及节点深度(eg:void DFS(int u,int fa,int depth))。(建议用邻接表的方式)遍历每一个儿子,同时初始化。
根据dfu定义,dfu[v][i+1]表示v的第2i+1代祖先,也就是第(2i+2i)代祖先。则可以先找到v的第2i代祖先u,再找到u的第2i代祖先。递推式如下:
dfu[v][i+1]=dfu[dfu[v][i]][i]; //dfu[v][i]表示v的第2i代祖先
由于递推过程中,dfu[v][i]已经计算出来了,我们就可以从dfu[v][0]出发推出所有dfu。
③计算LCA(u,v)
为了方便计算,先保证dep[v]>dep[u]。
要将u、v移动到同层,即把v上移(dep[u]-dep[v])层。由于我们知道v的2k代祖先,我们可以把dep[u]-dep[v]拆分成 2a1+2a2+...+2ap(C++实现可以判断 (dep[v]-dep[u])>>i&1,即2进制的(dep[v]-dep[u])的第i位是否为1),按次上移即可。
如果此时u=v,则说明最初u是v的祖先,则LCA(u,v)=u。
除开上述u=v的情况。由于 LCA(u,v) 的祖先一定也是u,v的公共祖先,所以我们可以将u、v上移到LCA(u,v)的下一层,即u,v的父亲为LCA(u,v)。从高到低枚举i(i最大为ceil(log2(n)),即退化为链后根与叶子节点),如果dfu[u][i]==dfu[v][i],则说明dfu[u][i]已经是LCA(u,v)或层数已经高于LCA(u,v)了,由于无法直接判断是否是LCA(u,v),我们就可以选择不上移(目标是将u,v转移到LCA(u,v)的下一层);如果dfu[u][i]!=dfu[v][i],则说明还没有到LCA(u,v),就可以上移。最后就可以移动到LCA(u,v)的下一层。
其实上述操作无非是令u,v到LCA(u,v)的距离为S,将S-1表示为2进制,再通过dfu顺次上移。最后得到dfu[u][0](或者dfu[v][0])就是LCA(u,v)了。
三、C++代码
初始化:
void DFS(int u,int fa,int depth)
{
dfu[u][]=fa;dep[u]=depth; //更新v的父节点以及深度
for(int i=;i<lnk[u].size();i++) //lnk是vector的邻接表
DFS(lnk[u][i],u,depth+);
}
void Prepare()
{
DFS(,-,); //先处理出节点的深度(dep)和直接祖先(dfu[0])
for(int i=;i+<;i++)
for(int j=;j<n;j++)
if(dfu[j][i]<) dfu[j][i+]=-; //上移位置已经超过根节点
else dfu[j][i+]=dfu[dfu[j][i]][i];
}
计算LCA:
int LCA(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v); //保证u不高于v
for(int i=;i<;i++) //拆分二进制
if(((dep[v]-dep[u])>>i)&) //上移到同一层
v=dfu[v][i];
if(u==v) return u; //u最初是v的根节点
for(int i=;i>=;i--)
if(dfu[u][i]!=dfu[v][i])
u=dfu[u][i],v=dfu[v][i];
return dfu[u][];
}
The End
Thanks for reading!
- Lucky_Glass
(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~)
【模板时间】◆模板·I◆ 倍增计算LCA的更多相关文章
- 【原创】洛谷 LUOGU P3379 【模板】最近公共祖先(LCA) -> 倍增
P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询 ...
- 倍增求lca模板
倍增求lca模板 https://www.luogu.org/problem/show?pid=3379 #include<cstdio> #include<iostream> ...
- 倍增求lca(模板)
定义LCA,最近公共祖先,是指一棵树上两个节点的深度最大的公共祖先.也可以理解为两个节点之间的路径上深度最小的点.我们这里用了倍增的方法求了LCA.我们的基本的思路就是,用dfs遍历求出所有点的深度. ...
- 洛谷P3379 【模板】最近公共祖先(LCA)(dfs序+倍增)
P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询 ...
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- 「LuoguP3379」 【模板】最近公共祖先(LCA)
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- 洛谷P3379 【模板】最近公共祖先(LCA)
P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交 讨论 题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有 ...
- 洛谷——P3379 【模板】最近公共祖先(LCA)
P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询 ...
- luogo p3379 【模板】最近公共祖先(LCA)
[模板]最近公共祖先(LCA) 题意 给一个树,然后多次询问(a,b)的LCA 模板(主要参考一些大佬的模板) #include<bits/stdc++.h> //自己的2点:树的邻接链表 ...
随机推荐
- python函数(四)
一.函数是什么? 函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,编程中的函数在英文中也有很多不同的叫法.在BASIC中叫做subroutine(子过程或子程序),在Pasc ...
- 如何解决 “the specified service is marked as deletion”
在部署windows service应用程序,突然发生了如下问题:“the specified service is marked as deletion”.导致windows service不能部署 ...
- IA-32e模式下的异常处理
系统异常处理 CPU如果调用系统异常处理程序 需要的数据结构 IDT_Table: 中断向量表, 在中断向量表中的每一项都是一个中断描述符(中断门或者陷阱门), 一个中断描述符中的几位是段选择符 GD ...
- CRLF注入攻击
原理:http数据包通过\r\n\r\n来分开http header何http body 实现:首先这种攻击发生在应用层,且发生在服务器返回给我们的http reponse没有经过敏感字符的过滤, ...
- [转]JAVA Iterator 的用法
java.util包中包含了一系列重要的集合类.本文将从分析源码入手,深入研究一个集合类的内部结构,以及遍历集合的迭代模式的源码实现内幕. 下面我们先简单讨论一个根接口Collection,然后分析一 ...
- AngularJS 学习(-)Hello world
早期的AngularJS使我们的前端开发模式发生很大的变化,基使用MVC. Model - html 模板:Controller - js脚本; Model 来自于Web API 或其他Service ...
- python JSON性能测试与simplejson对比
简单测试了一下,如果用JSON,也就是python2.6以上自带的json处理库,效率还算可以: 1K的数据,2.9GHz的CPU,单核下每秒能dump:36898次.大约是pyamf的5倍.但数据量 ...
- Linux远程桌面(一)
在机房折磨很久弄好的自己 Mark 一下.(测试环境rhel5.5) vnc 之独立服务配置 步骤一: (1)查看系统是否安装vnc服务(也可以在 系统-管理员-服务 里查看并勾选开机自启) # rp ...
- Win7无法连接wifi网络的解决方法
以下方法是一个笔记,不能保证100%解决问题 方法1. 在CMD命令窗口中, ipconfig /release ipconfig/renew 方法2. 右键点网络图标,troubleshoot pr ...
- 运行在 Android 系统上的完整 Linux -- Termux
Termux 可以在安卓系统上搭建一个完整的linux 环境,类似于 cygwin 并非linux 虚拟机,整个安装包只有 几百KB 刚开始觉得这东西的命令行很难用,看了官方介绍后才发现它原来有许多 ...