4月4日,应学弟要求去了次学校给小同学们讲了一堂课,其实讲的挺内容挺杂的,但是目的是引出LCA算法。

现在整理一下当天讲课的主要内容:

开始并没有直接引出LCA问题,而是讲了RMQ(Range Minimum/Maximum Query)问题。

RMQ指的是对于给定的一个数组,每一次询问一个区间[L,R]中数字的最小或最大值,一个很经典的问题。

一开始没有学生知道如何求解此问题。我将问题弱化,每一次询问[1,R]中数字的最值,有学生打出了求一个min/max数组即可,此为正解。

这是一个相当正确的做法,有许多学过线段树的同学在碰到这个弱化的问题时也会不假思索的无脑线段树,将简单的问题复杂化。学生能直接答出min/max数组,我认为也是因为他们现在会的算法还太少,基本没有干扰项。

我接下来介绍了ST表(Sparse Table)求解RMQ。

如果我们现在有一个二维数组f[i][j]表示从第i个数开始包括自己向后总共2^j个数字中的最值,如何求解RMQ?

底下有学生给了一种我觉得挺不错的思路:求出L与R的范围D,将D作二进制分解,则可以在log(D)的复杂度呢,通过若干个f数组中的值求解得出L与R之间数字的最值。

这个思路相当nice,但是我说,其实还是不够优秀,因为事实上只要至多2个数字便可以得出结果了。

如果D是一个2的幂,则只f[L][log2(D)]直接就是答案,否则答案为min/max(f[L][(int)log2(D)][R-2^((int)log2(D))+1][(int)log2(D)]),看上去有些复杂,其实写在代码中比较方便,含义就是求出最大的k使得2^k小于D,接着用左边和右边2段的f值一定能完整覆盖到整个范围区间。

实际上,上述两种情况可以归一化为第二种计算方式。

接下来的问题就是如何求解f数组。其实ST表本质上是一种动态规划的思想,首先初始化所有的f[i][0]为a[i],也即原来的值,接下去从1开始向上循环,直到超过log(n)。f[i][j]=min/max(f[i][j-1],f[i+2^(j-1)][j-1])

至此ST表算法讲授完毕。另外一点想提的是,很多人会把RMQ等同于ST表,这是一种误会。RMQ并不是一种算法,是区间最值问题,本质上是一类问题。RMQ当然也可以用线段树解决。甚至brute force也可以称为RMQ的一种算法。

接下去,我讲了有向图深度优先遍历中的四类边,本文不作展开。

最后,我引出了LCA问题。底下的学生没有人知道不用暴力怎么做。但是有的学生的暴力思路还是有些interesting的,例如先遍历一遍树,当发现第一个点后,在回溯时,标记走过的点的值+1,。再遍历一次树,当发现第二个点后,在回溯时,标记走过的点的值+1,第一次发现有个点值为2,则说明是两个点的最近公共祖先。

我觉得这个思路还是有点意思的,因为比较反常规。

我主要讲了三种求解LCA问题的算法。

第一种是将其归约至RMQ问题。如何规约呢?在遍历到一个节点与回溯到一个节点时,打时间戳,并记录这个时间戳对应的节点。则LCA问题可以规约为对应2个点第一次被遍历到的时间戳之间所有所有时间戳,哪个时间戳对应的节点的深度最小。

听起来有些复杂,其实想想就是那么回事情。

在问到如何将LCA规约至RMQ时,有一个学生意外地联想到了第二种方法,不过他没有完整地想出来。

第二种算法叫树上倍增,与ST表有异曲同工之处,f[i][j]表示节点i的第2^j个祖先。

则求解LCA的算法流程大致是这样的,两个点u和v,不妨假设u的深度大于等于v的深度,则让u向上到两点处于同一个深度。如果两个点相同,则直接返回答案。否则从大到小枚举2的幂,看是否两个点向上那么多个祖先是相同的,如果相同,则认为走过头了,continue掉。否则将其置为新的祖先点。最后2个点再向上走一次,也即他们的父亲节点就是LCA。这其中让u向上走到与v相同高度,用的方法与上文RMQ问题中学生提到的二进制分解是一个思路,即将深度差作二进制分解,向上跳log(d)次。

第三种算法是Tarjan发明的一种基于并查集的离线算法。

所谓离线即先一次性将所有的询问保存,对树做深度优先遍历,每一次遍历完之后,将子树对应的集合合并至当前节点。递归完所有子节点后,遍历所有与当前点相关的询问,如果另外一个点已经被访问过了,则这个询问的答案为另一个点所在集合的代表元。

最后我当时提了一下,没有深入讲,是用树链剖分求解LCA问题。有点杀鸡用宰牛刀的感觉了。

【算法】RMQ LCA 讲课杂记的更多相关文章

  1. poj3728之离线LCA+dp思想/RMQ+LCA(非常好的题目)

    题意很简单 给一个树(n < 5w) 每个点有个权值,代表商品价格 若干个询问(5w) 对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品,   ...

  2. tarjan算法求LCA

    tarjan算法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 这里我们使用tarjan算法离线算法解决这个问题. 离线 ...

  3. 【37.48%】【hdu 2587】How far away ?(3篇文章,3种做法,LCA之ST算法(RMQ))

    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...

  4. 【图论算法】LCA最近公共祖先问题

    LCA模板题https://www.luogu.com.cn/problem/P3379题意理解 对于有根树T的两个结点u.v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深 ...

  5. dfs序 + RMQ = LCA

    dfs序是指你用dfs遍历一棵树时,每个节点会按照遍历到的先后顺序得到一个序号.然后你用这些序号,可以把整个遍历过程表示出来. 如上图所示,则整个遍历过程为1 2 3 2 4 5 4 6 4 2 1 ...

  6. poj 1330(RMQ&LCA入门题)

    传送门:Problem 1330 https://www.cnblogs.com/violet-acmer/p/9686774.html 参考资料: http://dongxicheng.org/st ...

  7. 算法笔记--lca倍增算法

    算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...

  8. hdu 2874 Connections between cities(st&rmq LCA)

    Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  9. hihoCoder#1067(离线算法求LCA)

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中 ...

随机推荐

  1. F# 语法概览

    F#和C#的语法差别 语法上,F#和C#有两个主要差别: 用缩进而非花括号分隔代码块 用空白而非逗号分隔参数 F#常见语法元素 以下是F#代码中常见的语法元素 注释 // 这是单行注释 (* 这是多行 ...

  2. 1491: [NOI2007]社交网络

    1491: [NOI2007]社交网络 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 881  Solved: 518[Submit][Status] ...

  3. 《jQuery实战(第二版)》读书笔记

    第一部分 jQuery核心 1.jQuery基础 第一章总结了jquery的大致功能,基本原理,使用方式. point: (1).引入:<script type="text/javas ...

  4. git remote log error

    使用git pull的时候收到以下信息: error: there are still refs under 'refs/remotes/origin/xxxx'From 10.1.25.57:yyy ...

  5. Erlang模块gen_server翻译

    gen_server 概要: 通用服务器行为描述: 行为模块实现服务器的客户端-服务器关系.一个通用的服务器进程使用这个模块将实现一组标准的接口功能,包括跟踪和错误报告功能.它也符合OTP进程监控树. ...

  6. Cordova框架基本原理

    最近在做混编项目,也是从项目里开始接触Cordova框架,网上很多帖子都总结的很好,我还是要总结一下,便于加深一下. Cordova框架是一个可以让JS与原生代码(包括 Android 的 java, ...

  7. php数组的使用

    <?php echo "<h2>--------普通数组--------</h2>"; $arr1 = array(1,2,3,4); print_r ...

  8. 利用Arcgis for javascript API绘制GeoJSON并同时弹出多个Popup

    1.引言 由于Arcgis for javascript API不可以绘制Geojson,并且提供的Popup一般只可以弹出一个,在很多专题图制作中,会遇到不少的麻烦.因此本文结合了两个现有的Arcg ...

  9. Java实现读取文章中重复出现的中文字符串

    在上个星期阿里巴巴一面的时候,最后面试官问我如何把一篇文章中重复出现的词或者句子找出来,当时太紧张,答的不是很好.今天有时间再来亲手实现一遍.其实说白了也就是字符串的处理,所以难度并不是很大. 以下是 ...

  10. Repaints and Reflows 重绘和重排版

    当浏览器下载完所有页面HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据 一棵DOM树 表示页面结构 Normal 0 7.8 磅 0 2 false false fa ...