LCA,即树上两点之间的公共祖先,求这样一个公共祖先有很多种方法:

暴力向上:O(n)

每次将深度大的点往上移动,直至二者相遇

树剖:O(logn)

在O(2n)预处理重链之后,每次就将深度大的沿重链向上,直至二者在一条链上

tarjan_lca:离线O(n+m)

先记录所有的询问,对树进行一次dfs,对于搜索到的点u,先将点u往下搜,再将点u与父节点所在集合合并,之后对于它的所有询问(u,v),若v已被访问,那么找v所在集合的祖先e,则e就是u与v的lca

但我们今天要讲的是

倍增lca

所谓倍增,就是利用二进制将冗长的多个相同步骤合并,以实现加速转移的算法。
比如快速幂就是一个经典的例子,将多次乘法通过二进制合并

下面我们来讲讲如何利用倍增求LCA:
还记得上面的暴力算法吗?
暴力算法是最质朴的东西,却是所有高端操作的出发点,倍增就是这样。
我们只需要利用倍增,将往上移动的操作压缩,就能实现算法复杂度的优化

实现

我们设f[i][j]表示i节点的第2^j代祖先,这样f[i][0]就是i的父亲,而对于所有j>0,我们有
f[i][j] = f[f[i][j - 1]][j - 1]
怎么理解呢?
i节点的第2^(j - 1)代祖先的2^(j - 1)祖先就是i的第2^j代祖先
所以我们用O(nlogn)的时间就预处理出了f[][]数组

void dfs(int u,int fa){
dep[u] = dep[fa] + 1;
f[u][0] = fa;
for (int k = head[u]; k != -1; k = edge[k].next)
if (edge[k].to != fa)
dfs(edge[k].to,u);
}

求深度

void cal(){
for (int i = 1; (1<<i) <= N; i++)
for(int u = 1; u <= N; u++)
f[u][i] = f[f[u][i - 1]][i - 1];
}

预处理



求出了f数组,怎么求lca呢?
对于询问lca(u,v),不妨设u是其中深度较大的,我们先将u升到离v同样的深度,设需要向上移动d = dep[u] - dep[v]
我们把d看做二进制,那么我们只需对于其中所有的1进行操作就好了

比如说:
我们需要向上5次,即101,那么我们只需要向上转移100次和1次,这就刚好与f[i][2]和f[i][0]相对应

移到相同位置后,我们从二进制大到小枚举,当u和v祖先不同时,就向上转移,最后u的祖先就是lca
int lca(int u,int v){
if (dep[u] < dep[v]) swap(u,v);
int d = dep[u] - dep[v];
for (int i = 0; (1<<i) <= d; i++)
if ((1<<i) & d)
u = f[u][i];
if (u != v){
for (int i = (int)log(N); i >= 0; i--)
if (f[u][i] != f[v][i]){
u = f[u][i];
v = f[v][i];
}
return f[u][0];
}
else return u;
}

LCA的倍增算法的更多相关文章

  1. Lca 之倍增算法

    引入: 比如说要找树上任意两个点的路上的最大值.如果是一般的做法 会 接近o(n)的搜,从一个点搜到另一个点,但是如果询问多了复杂度就很高了. 然后我们会预处理.预处理是o(n²)的,询问是o(1)的 ...

  2. 关于树论【LCA树上倍增算法】

    补了一发LCA,表示这东西表面上好像简单,但是细节真挺多. 我学的是树上倍增,倍增思想很有趣~~(爸爸的爸爸叫奶奶.偶不,爷爷)有一个跟st表非常类似的东西,f[i][j]表示j的第2^i的祖先,就是 ...

  3. LCA(最近公共祖先)之倍增算法

    概述 对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 如图,3和5的最近公共祖先是1,5和2的最近公共祖先是4 在本篇中我们先介 ...

  4. LCA倍增算法

    LCA 算法是一个技巧性很强的算法. 十分感谢月老提供的模板. 这里我实现LCA是通过倍增,其实就是二进制优化. 任何一个数都可以有2的阶数实现 例如16可以由1 2 4 8组合得到 5可以由1 2 ...

  5. 最近公共祖先 LCA 倍增算法

          树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...

  6. 关于LCA的倍增解法的笔记

    emmmmm近日刚刚学习了LCA的倍增做法,写一篇BLOG来加强一下印象w 首先 何为LCA? LCA“光辉”是印度斯坦航空公司(HAL)为满足印度空军需要研制的单座单发轻型全天候超音速战斗攻击机,主 ...

  7. [模板]LCA的倍增求法解析

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  8. 【一个蒟蒻的挣扎】LCA (倍增)

    #include<cstdio> #include<iostream> #include<cstring> using namespace std; struct ...

  9. LCA树上倍增求法

    1.LCA LCA就是最近公共祖先(Least common ancestor),x,y的LCA记为z=LCA(x,y),满足z是x,y的公共祖先中深度最大的那一个(即离他们最近的那一个)qwq 2. ...

随机推荐

  1. equals和==方法比较(二)--Long中equals源码分析

    接上篇,分析equals方法在Long包装类中的重写,其他类及我们自定义的类,同样可以根据需要重新equals方法. equals方法定义 equals方法是Object类中的方法,java中所有的对 ...

  2. Spring学习(五)-----注入bean属性的三种方式( 1: 正常的方式 2: 快捷方式 3: “p” 模式)

    在Spring中,有三种方式注入值到 bean 属性. 正常的方式 快捷方式 “p” 模式 看到一个简单的Java类,它包含两个属性 - name 和 type.稍后将使用Spring注入值到这个 b ...

  3. php开发文章发布示例(正则表达式实例开发)

    存档: post.php <form method="post" action="viewthread.php" target="_blank& ...

  4. php实现图形计算器

    存档: index.php <html> <head> <title>图形计算器开发</title> <meta http-equiv=" ...

  5. testNG-失败用例重跑机制

    下面简单介绍下testNG的失败重跑的实现方法: 1.首先编写一个类,实现IRetryAnalyzer类,重写其中的retry方法. public class TestNGRetry implemen ...

  6. selenium 结合 docker 构建分布式测试环境 (初学者视角)

    前言:随着自动化测试越学越深,深深觉得有太多的东西需要总结. 1.记录下学习中遇到的坑,当做学习笔记.2.有前人路过看到文章中比较落后的做法,请务必一定要指教.(因为是初学者视角,很多东西只是走通而已 ...

  7. openvpn部署

    原文发表于cu:2016-03-29 参考文档: 安装:http://qicheng0211.blog.51cto.com/3958621/1575273 安装:http://www.ipython. ...

  8. [C++] Solve "Cannot run program "gdb": Unknown reason" error

    In Mac OSX, The Issue Image: 1. Build the project on Eclipse successfully. 2. Run gdb on command lin ...

  9. 从无到有之webpack+vuerouter的简单例子以及各个属性解释

    之前一直没玩过webpack和vue,近两周才看这玩意,本文纯属自己的实验+之前angular作战经验的理解一些入门文章 首先webpack关于vue以及各个包 module.exports = { ...

  10. Scrum立会报告+燃尽图(Beta阶段第七次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2388 项目地址:https://coding.net/u/wuyy694 ...