树上倍增求LCA及例题
先瞎扯几句
树上倍增的经典应用是求两个节点的LCA
当然它的作用不仅限于求LCA,还可以维护节点的很多信息
求LCA的方法除了倍增之外,还有树链剖分、离线tarjan ,这两种日后再讲(众人:其实是你不会吧:unamused:。。。)
思想
树上倍增嘛,顾名思义就是倍增
相信倍增大家都不默认,著名的rmq问题的$O(n*logn)$的解法就是利用倍增实现的
在树上倍增中,我们用
$f[j][i]$表示第$j$号节点,跳了$2^j$步所能到达的节点
$deep[i]$表示$i$号节点的深度
然后用这两个数组瞎搞搞就能整出LCA来啦
众人::wrench: :hammer: :hocho:
实现
deep&&f[i][0]
首先,$f[i][0]$(也就是一个节点的上面的节点)容易求得,只要对整棵树进行一边dfs就好,在dfs的时候我们顺便可以求出$deep$数组
for(int i=head[now];i!=-;i=edge[i].nxt)
if(!deep[edge[i].v])
deep[edge[i].v]=deep[now]+,f[edge[i].v][]=now,dfs(edge[i].v);
这段代码应该不难理解
f[j][i]
那么我们怎么维护$f$数组呢?
不难得到$f[j][i]=f[f[j][i-1]][i-1]$ 众人:难!
其实真的不难,一张图就可以解释明白啦
这句话的意思其实是说,一个节点跳$2^j$所能到达的节点实际上是跳$2^{i-1}$所能到达的节点再往上跳$2^{j-1}$步
注意$2^i=2^{i-1}+2^{i-1}$
代码:
for(int i=;i<=;i++)
for(int j=;j<=n;j++)
f[j][i]=f[f[j][i-]][i-];
LCA
接下来要进入最核心的部分啦,
我们如何用$deep$和$f$乱搞搞出$x$和$y$的LCA呢?
按照书上倍增算法的介绍
我们求LCA需要分为两步
设$deep[x]>deep[y]$
- 让$x$向上跳,跳到与$y$深度相同位置
- 让$x$和$y$同时向上跳,跳到祖先相同位置
根据二进制分解什么乱七八糟的,这么做一定是对的,其实这个挺显然的,yy一下就好了吧。。。
第一步
if(deep[x]<deep[y]) swap(x,y);
for(int i=;i>=;i--)
if(deep[f[x][i]]>=deep[y])
x=f[x][i];
首先处理一下$x$和$y$的深度,保证$deep[x]>deep[y]$
然后尽量让$x$向上跳就好啦,注意这里是可以取到等号的
注意这里可能会出现一种特殊情况
这个时候他们的最近公共祖先就是$y$
if(x==y) return x;
第二步
同时向上跳,直到祖先相同为止
那么此时他们再向上跳一步所能到达的节点就是LCA啦
for(int i=;i>=;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][];
怎么样?
是不是很简单?
完整代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = ;
inline void read(int &n) {
char c = getchar(); bool flag = ; n = ;
while (c < '' || c > '') c == '-' ? flag = , c = getchar() : c = getchar();
while (c >= '' && c <= '') n = n * + c - , c = getchar(); flag == ? n = -n : n = n;
}
struct node {
int v, nxt;
} edge[MAXN];
int head[MAXN];
int num = ;
inline void add_edge(int x, int y) {
edge[num].v = y;
edge[num].nxt = head[x];
head[x] = num++;
}
int f[MAXN][];
int deep[MAXN];
int n, m, root;
void dfs(int now) {
for (int i = head[now]; i != -; i = edge[i].nxt)
if (!deep[edge[i].v])
deep[edge[i].v] = deep[now] + , f[edge[i].v][] = now, dfs(edge[i].v);
}
void PRE() {
for (int i = ; i <= ; i++)
for (int j = ; j <= n; j++)
f[j][i] = f[f[j][i - ]][i - ];
}
int LCA(int x, int y) {
if (deep[x] < deep[y]) swap(x, y);
for (int i = ; i >= ; i--)
if (deep[f[x][i]] >= deep[y])
x = f[x][i];
if (x == y) return x;
for (int i = ; i >= ; i--)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][];
}
int main() { memset(head, -, sizeof(head));
read(n); read(m); read(root);
for (int i = ; i <= n - ; i++) {
int x, y; read(x); read(y);
add_edge(x, y);
add_edge(y, x);
}
deep[root] = ;
dfs(root);
PRE();
for (int i = ; i <= m; i++) {
int x, y;
read(x); read(y);
printf("%d\n", LCA(x, y));
}
return ;
}
例题
都是些入门难度的题目
洛谷P3379 【模板】最近公共祖先(LCA)
http://www.cnblogs.com/zwfymqz/p/6832524.html
POJ 1986 Distance Queries
http://www.cnblogs.com/zwfymqz/p/7791527.html
HDU 3078 Network
http://www.cnblogs.com/zwfymqz/p/7791617.html
HDU 2586 How far away ?
http://www.cnblogs.com/zwfymqz/p/7791517.html
树上倍增求LCA及例题的更多相关文章
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- 树上倍增求LCA(最近公共祖先)
前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...
- [算法]树上倍增求LCA
LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...
- Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)
题面 传送门 题目大意: 给定一个无向连通带权图G,对于每条边(u,v,w)" role="presentation" style="position: rel ...
- 树上倍增求LCA详解
LCA(least common ancestors)最近公共祖先 指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先. 定 ...
- [luogu3379]最近公共祖先(树上倍增求LCA)
题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...
- CF 519E(树上倍增求lca)
传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时, ...
- 树上倍增求LCA
大概思想就是,节点$i$的第$2^{j}$个父节点是他第$2^{j-1}$个父亲的第$2^{j-1}$个父亲 然后可以$O(nlogn)$时间内解决…… 没了? //fa[i][j]表示i的第2^j个 ...
- 【题解】洛谷P4180 [BJWC2010] 严格次小生成树(最小生成树+倍增求LCA)
洛谷P4180:https://www.luogu.org/problemnew/show/P4180 前言 这可以说是本蒟蒻打过最长的代码了 思路 先求出此图中的最小生成树 权值为tot 我们称这棵 ...
随机推荐
- 谈一谈从 Delphi 2009 之后就支援的重要功能 – 泛型 (Generic)
前言 在C++的语言基础当中,除了物件导向.事件驱动的概念之外,模版设计(Template)也是非常重要的一环.然而,C++的开发人员能够善用模版设计的并不多.模版设计这个好物,一般还有一个名称,就是 ...
- emWin表盘界面设计,含uCOS-III和FreeRTOS两个版本
第4期:简易表盘界面设计 配套例子:V6-906_STemWin提高篇实验_简易表盘界面设计(uCOS-III)V6-907_STemWin提高篇实验_简易表盘界面设计(FreeRTOS) 例程下载地 ...
- 2018,你与 i 春秋的故事都在这
年终岁末,深思回顾,过去的一年我们共同创造了很多回忆,有欢乐,有感动,更有收获.回首2018年,伴随着激情与挑战,我们共创了很多佳绩,一起来看看吧. 课程&实验 2018新增原创录制实战视频课 ...
- Android OpenGL ES 开发(四): OpenGL ES 绘制形状
在上文中,我们使用OpenGL定义了能够被绘制出来的形状了,现在我们想绘制出来它们.使用OpenGLES 2.0来绘制形状会比你想象的需要更多的代码.因为OpenGL的API提供了大量的对渲染管线的控 ...
- Javascript高级编程学习笔记(1)—— JS简介
此系列文章,用于记录所学,如有错误欢迎指出. Javascript组成 1.核心(ECMAScript) 2.文档对象模型(DOM) 3.浏览器对象模型(BOM) 1.核心(ECMAScript) E ...
- SUSE12Sp3-Supervisor 守护.net core进程
1.安装setuptools 将setuptools-0.6c11.tar.gz安装包放到服务器上 tar zxvf setuptools-0.6c11.tar.gz cd setuptools-0. ...
- [Java]LeetCode133. 克隆图 | Clone Graph
Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. OJ's ...
- [Swift]LeetCode863. 二叉树中所有距离为 K 的结点 | All Nodes Distance K in Binary Tree
We are given a binary tree (with root node root), a targetnode, and an integer value K. Return a lis ...
- dataframe去除null、NaN和空字符串
去除null.NaN 去除 dataframe 中的 null . NaN 有方法 drop ,用 dataframe.na 找出带有 null. NaN 的行,用 drop 删除行: import ...
- 开发环境配置:jdk8的详细安装教程&&tomact的详细安装教程&&java环境变量的配置&&tomcat启动总失败原因
1.下载 链接: http://pan.baidu.com/s/1i57HZKx 密码: cnb4 2.详细安装过程 3.下载地址 链接: http://pan.baidu.com/s/1mi6VUp ...