LCA指的是最近公共祖先(Least Common Ancestors),如下图所示:

  4和5的LCA就是2

  那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度

  然后把深度更深的那一个点(4)一个点地一个点地往上跳,直到到某个点(3)和另外那个点(5)的深度一样

然后两个点一起一个点地一个点地往上跳,直到到某个点(就是最近公共祖先)两个点“变”成了一个点

  不过有没有发现一个点地一个点地跳很浪费时间?

如果一下子跳到目标点内存又可能不支持,相对来说倍增的性价比算是很高的

  倍增的话就是一次跳2i 个点,不难发现深度差为x时,深度更深的那个点就需要跳x个点

于是可以写出这段代码

 if(depth[a] < depth[b])    swap(a, b);
int c = depth[a] - depth[b];
for(int i = ; i <= ; i++){
if(c & ( << i)){
a = up[a][i];
}
}

  接下来很快就会发现一个很严重的问题:两个点按照这样跳,不能保证一定是最近的

所以倍增找lca的方法是这样的:

  从最大可以跳的步数开始跳(一定是2i),如果跳的到的位置一样,就不跳,如果不一样才跳,每次跳的路程是前一次的一半

  过程大概就像上图所示,但是执行完了这一段到的点不是最近公共祖先,但是,它们再往上跳一格,就到了

把这一段写成代码,就成了这样:

 for(int i = ; i >= ; i--){
if(up[a][i] != up[b][i]){
a = up[a][i];
b = up[b][i];
}
}

  前面还需要加上一句特判(当a和b在同一边时,深度浅的那个点就是最近公共祖先)

if(a == b)    return a;

  好了,会求lca了,关键是怎么构造倍增数组。

没有疑问的是向上跳一格就是自己的父节点

f[i][] = fa[i];

  这个是初值,接着可以根据这个推出来其他的,除此之外还要附上初值0,不然有可能会RE

f[i][j] = f[f[i][j - ]][j - ];

  就是把这一段路,分成两段已经知道的

  完整代码就是这样的:

 Matrix<int> up;
inline void init_bz(){
up = Matrix<int>(, n + );
memset(up.p, , sizeof(int) * * (n + ));
for(int i = ; i <= n; i++){
up[i][] = fa[i];
}
for(int j = ; j <= ; j++){
for(int i = ; i <= n; i++){
up[i][j] = up[up[i][j - ]][j - ];
}
}
}

  注意倍增求LCA适用于询问多的情况,不然光在预处理上花的时间就已经够多了(如果只有一两个询问,直接暴力就好了)

  当然,这个倍增算法判断条件是若干级祖先是否相等。

  同样,点$u$,$v$的LCA还满足它是其中一个点的最近的一个祖先,满足$u$,$v$都在它的子树中。

  判断一个点是否在另一个点的子树中,我们可以用dfs序来判断。

  这是倍增的另一种判断方法:

 1 void dfs(int p, int fa) {
2 bz[p][0] = fa, in[p] = ++cnt;
3 for (int i = 1; i < bzmax; i++)
4 bz[p][i] = bz[bz[p][i - 1]][i - 1];
5 for (int i = g.h[p]; ~i; i = g[i].nx) {
6 int e = g[i].ed;
7 if (e == fa) continue;
8 dfs(e, p);
9 }
10 out[p] = cnt;
11 }
12
13 int lca(int a, int b) {
14 if (dep[a] > dep[b]) swap(a, b);
15 if (in[a] <= in[b] && out[a] >= out[b])
16 return a;
17 for (int i = bzmax - 1, nx; ~i; i--) {
18 nx = bz[a][i];
19 if (!(in[nx] <= in[b] && out[nx] >= out[b]))
20 a = nx;
21 }
22 return bz[a][0];
23 }
24

[算法]树上倍增求LCA的更多相关文章

  1. [学习笔记] 树上倍增求LCA

    倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...

  2. 树上倍增求LCA(最近公共祖先)

    前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...

  3. 树上倍增求LCA及例题

    先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...

  4. Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)

    题面 传送门 题目大意: 给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: rel ...

  5. 树上倍增求LCA详解

    LCA(least common ancestors)最近公共祖先 指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先. 定 ...

  6. [luogu3379]最近公共祖先(树上倍增求LCA)

    题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...

  7. [算法模板]倍增求LCA

    倍增LCA \(fa[a][i]\)代表a的第\(2^{i}\)个祖先. 主体思路是枚举二进制位,让两个查询节点跳到同一高度然后再向上跳相同高度找LCA. int fa[N][21], dep[N]; ...

  8. CF 519E(树上倍增求lca)

    传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时, ...

  9. 树上倍增求LCA

    大概思想就是,节点$i$的第$2^{j}$个父节点是他第$2^{j-1}$个父亲的第$2^{j-1}$个父亲 然后可以$O(nlogn)$时间内解决…… 没了? //fa[i][j]表示i的第2^j个 ...

随机推荐

  1. linux服务之irqbalance

    irqbalance 理论上:启用 irqbalance 服务,既可以提升性能,又可以降低能耗.irqbalance 用于优化中断分配,它会自动收集系统数据以分析使用模式,并依据系统负载状况将工作状态 ...

  2. fatal error: Invalid layout of preloaded class

    今天很奇怪,在java类中写一个单纯的main函数,打印一句话竟然出不来,后来各种资料找,终于知道解决的方法了,如下 错误截图: 解决方法: (1)选中你要运行的那个类,点击上方的project--p ...

  3. Verilog HDL那些事_建模篇笔记(实验一,实验二)

    实验一:永远的流水灯 扫描频率配置为100Hz,即是说扫描周期为10ms.这里需要注意的是扫描周期的概念.流水灯嘛,顾名思义,扫描周期指的是流水灯扫一轮所需要的时间.听到说周期,就应该想到在建模的时候 ...

  4. [solr] - 环境搭建

    这里忽略java安装和tomcat安装,这里使用的是solr-4.10.0 1.到apache下载solr,地址: http://mirrors.hust.edu.cn/apache/lucene/s ...

  5. ruby 查询mysql方法

    首先对需要使用的数据库进行封装,便于使用:数据库表封装源码: mysqlapi.rb #业务涉及的数据库的配置ActiveRecord::Base$db1={:adapter => " ...

  6. [家里蹲大学数学杂志]第235期$L^p$ 调和函数恒为零

    设 $u$ 是 $\bbR^n$ 上的调和函数, 且 $$\bex \sen{u}_{L^p}=\sex{\int_{\bbR^n}|u(y)|^p\rd y}^{1/p}<\infty. \e ...

  7. JavaScript 在不刷新或跳转页面的情况下改变当前浏览器地址栏上的网址

    JavaScript 在不刷新或跳转页面的情况下改变当前浏览器地址栏上的网址 var stateObject = {}; var title = "改变后的网址的标题"; var ...

  8. OpenExplorer打开文件夹

    Eclipse中文件打开功能,可以到https://github.com/samsonw/OpenExplorer/downloads下载最新版本,将jar包放到eclipse的plugins目录中, ...

  9. SpringBoot之springfox(Swagger) (ApiDoc接口文档)

    Springfox的前身是swagger-springmvc,是一个开源的API doc框架,可以将我们的Controller的方法以文档的形式展现,基于Swagger. 官网地址:http://sp ...

  10. JS和CSS关于大小写的区分

    方法: document.getElementById("xx").style.xxx中的所有属性是什么 盒子标签和属性对照 CSS语法(不区分大小写) JavaScript语法( ...