LCA(Least Common Ancestors)是指树结构中两个结点的最低的公共祖先。而LCA算法则是用于求两个结点的LCA。当只需要求一对结点的LCA时,我们很容易可以利用递归算法在O(n)的时间复杂度内实现,其中n为树中的结点数目。但是有时候我们会要求计算多组结点对各自的LCA,这样总的时间复杂度将会到达O(nq),其中q为问题总数,这往往是无法接受的。

  LCA离线算法用于计算一组预先给出的结点对各自的LCA(即我们允许在拥有所有结点对信息之后再进行解答)。Tarjan算法即是一种LCA离线算法。我们需要为结点维护一下属性:实现并查集所需要的属性p和rank,children用于保存所有直接孩子结点,ancestor用于记录结点的祖先,visited用于记录结点是否已经访问过,questionList用于记录与node相关的LCA问题。

 tarjan(node)
for child in node.children
tarjan(child)
union(child, node)
node.findSet().ancestor = node
node.visited = true
for question in node.questionList
other = another node specified by question //将other设定为question中涉及到的另外一个问题
if(other.visited)
question.answer = other.findSet().ancestor

  上面就是LCA的所有部分了。其中union和findSet分别用于合并并查集以及查找结点所在并查集的代表结点。要解决所有的LCA问题,只需要用树中的根结点调用tarjan函数即可。

  先说明时间复杂度,由于并查集的所有操作摊还代价都可以视作为O(1),因此tarjan函数的2~6行实际上就是一个普通的深度优先搜索而已,其时间复杂度为O(n)。而第7~10行每次循环都会扫描一个问题,且每个问题只涉及两个结点,故最多只会被扫描两次,因此只会被调用O(q)次,故tarjan函数总共花费的时间复杂度为O(n+q),这无疑是优秀的时间复杂度。

  再说明算法的正确性:只需要说明每个问题都被正确求解了。从两方面说明,1.每个问题的answer属性都被设置过.2.每次对问题的answer属性进行设置时,其值总是正确的。

  由于每个问题都会被扫描两次,在第一次扫描结束后,之后会执行第6行将结点设置为已访问。而在第二次扫描时,发现另外一个结点已经被访问过了,因此会执行第10行代码,对answer属性进行设置。因此我们保证每个问题都会被解答,且以第二次扫描时的答案为最终答案。因此方面1被成功证明。

  假设问题Q问的是结点u和v的LCA,并且假设其LCA为a。分三种情况讨论,1是u=v,2是u!=v=a,3是u、v、a三者均不同。当情况1发生时,u在对所有孩子递归完后,扫描涉及自身的问题时,会将问题解决两次,而每次都将答案设置为自身,故这种情况下赋值是正确的。当第2种情况发生时,我们在v对孩子进行递归完毕后,会将所有孩子都合并到v所在的集合中,并将v所在集合的代表结点的祖先设置为v。而之后扫描问题时会遇到Q,此时由于u已经被访问过了,会将答案设置为v,此时答案是正确的(并且对该问题的第二次扫描已经完成,问题不会被重复赋值)。对于情况3,不妨设u在v之前被访问。由于a是u和v的LCA,因此a的任意子结点都不可能是u和v的LCA,即u和v挂在a的两个不同的子结点下。故当我们访问完u,并回溯到a时,会将u加入到a所在集合,并将a所在集合的祖先设置为a。而之后搜索到v后,v在扫描到Q时,会将Q的answer值设置为u.findSet().ancestor,此时u依旧处于a所在集合中,而a所在集合的祖先始终为a(因为第4~5行代码只会将以当前结点为根的子树中的结点加入到自身所代表的集合中,而由于a的流程尚未走完),因此answer为a,答案正确。  

Tarjan的LCA离线算法的更多相关文章

  1. LCA离线算法Tarjan详解

    离线算法也就是需要先把所有查询给保存下来,最后一次输出结果. 离线算法是基于并查集实现的,首先就是初始化P[i] = i. 接下来对于每个点进行dfs: ①首先判断是否有与该点有关的查询,如果当前该点 ...

  2. poj1330+hdu2586 LCA离线算法

    整整花了一天学习了LCA,tarjan的离线算法,就切了2个题. 第一题,给一棵树,一次查询,求LCA.2DFS+并查集,利用深度优先的特点,回溯的时候U和U的子孙的LCA是U,U和U的兄弟结点的子孙 ...

  3. Closest Common Ancestors---poj1470(LCA+离线算法)

    题目链接:http://poj.org/problem?id=1470 题意是给出一颗树,q个查询,每个查询都是求出u和v的LCA:    以下是寻找LCA的预处理过程: void LCA(u){ f ...

  4. HDU 2874 Connections between cities(LCA离线算法实现)

    http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意: 求两个城市之间的距离. 思路: LCA题,注意原图可能不连通. 如果不了解离线算法的话,可以看我之 ...

  5. 距离LCA离线算法Tarjan + dfs + 并查集

    距离B - Distance in the Tree 还是普通的LCA但是要求的是两个节点之间的距离,学到了一些 一开始我想用带权并查集进行优化,但是LCA合并的过程晚于离线计算的过程,所以路径长度会 ...

  6. LCA离线算法Tarjan的模板

    hdu 2586:题意:输入n个点的n-1条边的树,m组询问任意点 a b之间的最短距离 思路:LCA中的Tarjan算法,RMQ还不会.. #include <stdio.h> #inc ...

  7. HDU 2874 LCA离线算法 tarjan算法

    给出N个点,M条边.Q次询问 Q次询问每两点之间的最短距离 典型LCA 问题   Marjan算法解 #include "stdio.h" #include "strin ...

  8. POJ1986 DistanceQueries 最近公共祖先LCA 离线算法Tarjan

    这道题与之前那两道模板题不同的是,路径有了权值,而且边是双向的,root已经给出来了,就是1,(这个地方如果还按之前那样来计算入度是会出错的.数据里会出现多个root...数据地址可以在poj的dis ...

  9. 1128 - Greatest Parent---LightOj(LCA+离线算法)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1128 给你一颗树,树的每个节点都有一个权值,树根是节点0,权值为1,树中每个节点的权值 ...

随机推荐

  1. 人生苦短之我用Python篇(线程/进程、threading模块:全局解释器锁gil/信号量/Event、)

    线程: 有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.是一串指令的集合.线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是 ...

  2. MySQL免安装版安装配置、修改密码

    一:MySQL的下载安装 1.1 下载 我下载的是 ZIP Archive 版的,win7 64位的机器支持使用,而且相对而言,简单.干净. 首先,进入MySQL的官方网址,依次点击Downloads ...

  3. 重温CLR(四)基元类型、引用类型、值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到framework类型(fcl)中存在的类型. 下表列出fcl类型 从另一个角度,可以认为C ...

  4. 转载 TCPIP学习笔记之概述

    1.分层 网络协议通常分不同层次进行开发,每一层分别负责不同的通信功能.一个协议族,比如 T C P / I P,是一组不同层次上的多个协议的组合. T C P / I P通常被认为是一个四层协议系统 ...

  5. Pod Installing openssl-ios-bitcode报错

    pod update --no-repo-update 问题:执行上面的操作之后,pod在安装ssl的时候会报错.部分报错信息如下: localhost:OpenSSLTest Later$ pod ...

  6. junit基础学习

    学习地址一:http://blog.csdn.net/andycpp/article/details/1327147/ 学习地址二:http://blog.csdn.net/zen99t/articl ...

  7. debian的bt下载工具

    sudo apt install qbittorrent 如果觉得下载慢,可以选择下载的文件,在trackers部分修改 https://github.com/ngosang/trackerslist

  8. nodejs 中的 NODE_PATH

    在使用 nodejs 开发中我们都免不了要去安装一些第三方模块. 那么你或多或少的遇到过以下一些问题 再继续阅读之前,我们先来弄清楚一个概念. npm install --global xxx 属于全 ...

  9. 【转】Apache Jmeter发送post请求

    下面用Jmeter发送一个post请求, 对应的js代码如下: $("#register_a").click(function() { var name = $("#un ...

  10. 全文检索引擎Solr系列——整合中文分词组件IKAnalyzer

    IK Analyzer是一款结合了词典和文法分析算法的中文分词组件,基于字符串匹配,支持用户词典扩展定义,支持细粒度和智能切分,比如: 张三说的确实在理 智能分词的结果是: 张三 | 说的 | 确实 ...