首先安利自己倍增求LCA的博客,前置(算不上)知识在此。

LCA有3种求法:倍增求lca(上面qwq),树链剖分求lca(什么时候会了树链剖分再说。),还有,标题。

是的你也来和我一起学习这个了qwq。

开始吧。

众所周知,每当你dfs时,你都能产生一棵dfs树,可以根据你的dfs序来构建。

such as(丑陋的画风):

一个dfs的顺序。

以这个为例:

那么我们写出他的遍历顺序:

假如我们要求3,8(wtf?)的LCA,

那么我们首先写出他的bfs序:

123432565217871。

然后留意一下我们要求的两个数的位置。

123432565217871。

我们发现这样一个事情

两个数的LCA,一定在前一个数最后一次出现的位置(在bfs序中)。

感性证明

对于前一个数最后一次出现的位置,他的意义就是当前节点的子树已经遍历完了,并且正在进行回溯!(拍桌,划重点!)。

也就是说,他要回溯到他的祖先了,而它的祖先同样也是后一个节点的祖先,一定在后一个节点遍历前,前一个节点回溯后。

前一个节点<lca<后一个节点。

证毕。

那么,我们只要找到dfs遍历顺序中的 “前一个数最后一次出现的位置,后一个数第一次出现的位置”,这个区间取出区间最小值,即是两个节点的lca。

或许有人会说:为什么最小值一定是lca呢?

又需要证明了。

我们从几何学的角度来解释:

由图可知,两个节点分别在LCA的两个不同的子树中。

当A节点最后一次遍历完,经过一系列回溯,一定能回溯的LCA。

但是因为LCA的子树没有遍历完(链式存图i=edge[i].to),所以它只会遍历到LCA,然后继续遍历lca的子树直到遍历到B点。

感性证毕。

内么,区间最小值且不带修改的我们很容易想到st表

所以,dfs序+RMQ求LCA成立。

复杂度nlogn查询+o1查询,三者最优。

代码:

#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + ;
vector<int> g[M] ;
int n ; vector<int> vs ;//dfs order
int tot ;
int orm[M] ;
int id[M] ;
int dep[M] ; int d[M][] ;//RMQ
void dfs (int o , int u ,int DEP) {
int tmp = tot ++ ;
dep[u] = DEP ;
id[u] = vs.size () ;
orm[tmp] = u ;
vs.push_back (tmp) ; for (int i = ; i < g[u].size () ; i ++) {
int v = g[u][i] ;
if (v == o) continue ;
dfs (u , v , DEP + ) ;
}
int len = vs.size () ;
if (vs[len-] == tmp) vs.push_back (vs[id[o]]) ;
else vs.push_back (tmp) ;
} void init_RMQ () {
for (int i = ; i < *n- ; i ++) d[i][] = vs[i] ;
for (int j = ; ( << j) <= n ; j ++) {
for (int i = ; i + ( << j) <= n ; i ++) {
d[i][j] = min (d[i][j-] , d[i+(<<(j-))][j-]) ;
}
}
} int RMQ (int l , int r) {
printf ("l = %d , r = %d\n" , l , r ) ;
int k = ;
while ( (<<(k+)) <= r - l + ) k ++ ;
int tmp = min (d[l][k] , d[+r-(<<k)][k]) ;
return orm[tmp] ;
}
void Print () {
for (int i = ; i < *n- ; i ++) printf ("%3d " , i ) ; puts ("") ;
puts ("dfs order:") ;
for (int i = ; i < *n- ; i ++) printf ("%3d " , vs[i]) ; puts ("") ;
puts ("deep:") ;
for (int i = ; i < n ; i ++) printf ("%3d " , dep[i]) ; puts ("") ;
puts ("id :") ;
for (int i = ; i < n ; i ++) printf ("%3d " , id[i]) ; puts ("") ;
} void LCA () {
dfs (,,) ;
init_RMQ () ;
Print () ;
} int main () {
cin >> n ;
for (int i = ; i < n - ; i ++) {
int u , v ;
cin >> u >> v ;
g[u].push_back (v) ;
g[v].push_back (u) ;
}
LCA () ;
int Q ;
cin >> Q ;
while (Q --) {
int u , v ;
cin >> u >> v ;
if (id[u] > id[v]) swap (u , v ) ;
int ans = RMQ (id[u] , id[v]) ;
printf ("The %d and %d the lastest ans is %d , and they are away from %d\n" , u , v , ans , dep[u]+dep[v]-*dep[ans]) ;
}
return ;
}

——lyfdalao

完结。

dfs序+RMQ求LCA详解的更多相关文章

  1. hdu 2586 欧拉序+rmq 求lca

    题意:求树上任意两点的距离 先说下欧拉序 对这颗树来说 欧拉序为 ABDBEGBACFHFCA 那欧拉序有啥用 这里先说第一个作用 求lca 对于一个欧拉序列,我们要求的两个点在欧拉序中的第一个位置之 ...

  2. 树上倍增求LCA详解

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

  3. RMQ求LCA

    题目链接 rmq求LCA,interesting. 一直没有学这玩意儿是因为CTSC的Day1T2,当时我打的树剖LCA 65分,gxb打的rmq LCA 45分... 不过rmq理论复杂度还是小一点 ...

  4. BZOJ1906树上的蚂蚁&BZOJ3700发展城市——RMQ求LCA+树链的交

    题目描述 众所周知,Hzwer学长是一名高富帅,他打算投入巨资发展一些小城市. Hzwer打算在城市中开N个宾馆,由于Hzwer非常壕,所以宾馆必须建在空中,但是这样就必须建立宾馆之间的连接通道.机智 ...

  5. 【RMQ】洛谷P3379 RMQ求LCA

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

  6. dfs序 + RMQ = LCA

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

  7. LOJ#137. 最小瓶颈路 加强版(Kruskal重构树 rmq求LCA)

    题意 三倍经验哇咔咔 #137. 最小瓶颈路 加强版 #6021. 「from CommonAnts」寻找 LCR #136. 最小瓶颈路 Sol 首先可以证明,两点之间边权最大值最小的路径一定是在最 ...

  8. Codeforces 916E(思维+dfs序+线段树+LCA)

    题面 传送门 题目大意:给定初始根节点为1的树,有3种操作 1.把根节点更换为r 2.将包含u,v的节点的最小子树(即lca(u,v)的子树)所有节点的值+x 3.查询v及其子树的值之和 分析 看到批 ...

  9. LCA详解

    LCA,即最近公共祖先,在图论中应用比较广泛. LCA的定义如下:给定一个有根树,若节点$z$同时是节点$x$和节点$y$的祖先,则称$z$是$x,y$的公共祖先:在$x,y$的所有公共祖先当中深度最 ...

随机推荐

  1. 无监督异常检测之LSTM组成的AE

    我本来就是处理时间序列异常检测的,之前用了全连接层以及CNN层组成的AE去拟合原始时间序列,发现效果不佳.当利用LSTM组成AE去拟合时间序列时发现,拟合的效果很好.但是,利用重构误差去做异常检测这条 ...

  2. Docker不完全使用指南

    Docker官方文档:https://docs.docker.com/ 神马是Docker? Docker可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器. 开发者在笔记本上编译测试通过 ...

  3. Ubuntu更换科大源

    更换科大源 方案一:在命令行输入 sudo gedit /etc/apt/sources.list ,打开系统自带源文件. 将文件内源删除,更换为以下科大源: deb http://mirrors.a ...

  4. Java实现AES对称加密算法

    Java代码实现 import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGener ...

  5. PHP+jQuery.photoClip.js支持手势的图片裁剪上传实例

    PHP+jQuery.photoClip.js支持手势的图片裁剪上传实例,在手机上双指捏合为缩放,双指旋转可根据旋转方向每次旋转90度,在电脑上鼠标滚轮为缩放,双击则顺时针旋转90度. 下面让我们来看 ...

  6. selenium 鼠标,键盘操作

    1.打开和关闭网页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/usr/bin/env python # -*- coding:u ...

  7. T100——英文版凭证报表

    范例:cxrr001 效果:增加英文版报表选择 1.azzi301,复制cxrr001_g01,把样板编号改为cxrr001_g01_01: 2.下载cxrr001_g01的GR样板,把cxrr001 ...

  8. 菜单ACTION控制栏位字段编辑,点击菜单ACTION才能编辑指定的栏位

    范例(axmt500): 目的,控制新增的栏位(价格清单2),需点击菜单栏“修改价格清单2”才能对相应的栏位进行编辑修改,并记录修改人.日期: 1)在规格上增加新ACTION——action_modi ...

  9. (转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。

    ( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...

  10. 最全最新java面试题系列全家桶(带答案)

    最全最新java面试题系列全家桶(带答案) 置顶 2019年04月06日 22:40:28 青春季风暴 阅读数 14082 文章标签: java面试题技术栈 更多 分类专栏: 面试   版权声明:本文 ...