倍增LCA学习笔记
前言
“倍增”,作为一种二进制拆分思想,广泛用于各中算法,如$ST$表,求解$LCA$等等...今天,我们仅讨论用该思想来求解树上两个节点的$LCA$(最近公共祖先)
“倍增”是什么东西?
倍增就是“成倍增加”的意思,比如$1$倍增后变成了$2$,$2$倍增后就变成了$4$,$4$变成$8$,以此类推...
实现
一直向上LCA
在讲真正的倍增之前,我们先来说说最朴素的$LCA$,对于需要求解的两个点$(x,y)$,我们最先能想到的方法就是两个点先到达同一深度,然后一直往上跳父亲,知道两个点跳到同一个点上,这个点就是$LCA$。
int LCA (int x, int y) {
if (depth[x] < depth[y]) swap(x, y);
while(depth[x] != depth[y]) x = fa[x];
while(x != y) x = fa[x], y = fa[y];
return x;
}
不难发现,这种算法的时间开销很大,我们想办法来优化它。
倍增LCA
就如同$ST$表一样,我们不妨设$f[i][j]$表示树上编号为$i$的节点向上跳$2^j$个节点后所达到的节点,如同$ST$表的预处理,我们很容易发现如何预处理出这个$f$数组:
f[i][j] = f[f[i][j-1]][j-1];
显然,$i$往上跳$2{j-1}$次之后再跳$2{j-1}$次之后就相当于$i$往上跳$2^j$次,我们可以借此来优化,利用二进制优化背包的思想那样,将跳的次数二进制拆分。
于是,我们改写一下之前的代码
int LCA (int x, int y) {
if (depth[x] < depth[y]) swap(x, y);
for (int i = LogN; i >= 0; --i)
if (depth[f[x][i]] >= depth[y])
x = f[x][i];
if (x == y) return x;
for (int i = LogN; i >= 0; --i)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
这样一来,速度就快很多了,由原来的$O(Depth)$变成了现在的$O(log_2(Depth))$
代码
#include <cstdio>
#include <cstring>
typedef int ll;
const ll N = 5e5 + 10, M = 5e5 + 10, LogN = 25;
ll n, m, s, depth[N], f[N][LogN], a, b, c;
ll from[N], to[M << 1], nxt[M << 1], cnt, tmp, Log[N];
inline void swap (ll &a, ll &b) {tmp = a, a = b, b = tmp;}
//链式前向星加边
void addEdge (ll u, ll v) {
to[++cnt] = v, nxt[cnt] = from[u], from[u] = cnt;
}
//计算深度&计算祖先
void doit (ll u, ll fa) {
depth[u] = depth[fa] + 1;
for (register ll i = 1; i <= Log[n]; ++i) {
if ((1 << i) >= depth[u]) break;
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (register ll i = from[u]; i; i = nxt[i]) {
ll v = to[i];
if (v == fa) continue;
f[v][0] = u;
doit (v, u);
}
}
//计算LCA
inline ll LCA (ll x, ll y) {
if (depth[x] < depth[y]) swap(x, y);
//我们默认x为更深的那个点
for (register ll i = 0; i <= Log[n]; ++i)
if (depth[f[x][i]] >= depth[y])
x = f[x][i];
//将x跳到和y同一深度上
if (x == y) return x;
for (register ll i = Log[n]; i >= 0; --i)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
//一起向上跳
return f[x][0];
//不难看出,此时两个点均在其LCA的下方,往上跳一次即可
}
int main () {
scanf ("%d%d", &n, &m);//n节点数 m询问次数
Log[0] = -1;
for (register ll i = 1, u, v; i < n; ++i) {
scanf ("%d%d", &u, &v);
addEdge (u, v); addEdge(v, u);
Log[i] = Log[i >> 1] + 1;
}
Log[n] = Log[n >> 1] + 1;
doit (1, 0);
while (m--) {
scanf ("%d%d", &a, &b);
printf ("%d\n", LCA(a, b)));
}
return 0;
}
倍增LCA学习笔记的更多相关文章
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- LCA学习笔记
写在前面 目录 一.LCA的定义 二.暴力法求LCA 三.倍增法求LCA 四.树链剖分求LCA 五.LCA典型例题 题目完成度 一.LCA的定义 LCA指的是最近公共祖先.具体地,给定一棵有根树,若结 ...
- 树链剖分 树剖求lca 学习笔记
树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...
- 关于LCA的倍增解法的笔记
emmmmm近日刚刚学习了LCA的倍增做法,写一篇BLOG来加强一下印象w 首先 何为LCA? LCA“光辉”是印度斯坦航空公司(HAL)为满足印度空军需要研制的单座单发轻型全天候超音速战斗攻击机,主 ...
- kruskal重构树学习笔记
\(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal\) 求最小(大)生成树,树上求 \(lca\). 算法详 ...
- 「洛谷3292」「BZOJ4568」「SCOI2016」幸运数字【倍增LCA+线性基+合并】
[bzoj数据下载地址]不要谢我 先讲一下窝是怎么错的... \(MLE\)是因为数组开小了.. 看到异或和最大,那么就会想到用线性基. 如果不会线性基的可以参考一下我的学习笔记:「线性基」学习笔记a ...
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
- 【学习笔记】Kruskal 重构树
1. 例题引入:BZOJ3551 用一道例题引入:BZOJ3551 题目大意:有 \(N\) 座山峰,每座山峰有他的高度 \(h_i\).有些山峰之间有双向道路相连,共 \(M\) 条路径,每条路径有 ...
随机推荐
- consul windows安装
Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开 ...
- Sass 颜色函数
/* * Sass 颜色函数 * RGB 颜色函数 * 1. rgb($red,$green,$blue):根据红.绿.蓝三个值创建一个颜色: * rgb(200,40,88) //根据r:200,g ...
- 【BZOJ1560】【JSOI2009】火星藏宝图 [DP]
火星藏宝图 Time Limit: 10 Sec Memory Limit: 64 MB[Submit][Status][Discuss] Description Input Output Samp ...
- JVM在遇到OOM(OutOfMemoryError)时生成Dump文件
方法一: 命令:jmap -dump:format=b,file=heap.bin file:保存路径及文件名pid:进程编号(windows通过任务管理器查看,linux通过ps aux查看) du ...
- Java垃圾收集算法
算法名称 过程 优缺点 1. 标记-清除算法 (Mark-Sweep) 分为两个阶段: 1.首先标记出所有需要回收的对象: 2.在标记完成后统一回收所有被标记的对象. 缺点: 1.效率问题:标记和清除 ...
- linux编程之文件操作
在linux下用文件描述符来表示设备文件盒普通文件,文件描述符是一个整型的数据,所有对文件的操作都是通过文件描述符来实现的. 文件描述符是文件系统中连接用户空间和内核空间的枢纽,当我们打开一个或者创建 ...
- 中断中处理延时及一些函数的调用规则(中断调i2c驱动有感)--中断中的延迟delay与printk函数的冲突【转】
转自:http://blog.csdn.net/psvoldemort/article/details/8222371 1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,m ...
- mybaits-spring demo 记
代码:https://github.com/bobby96333/demo_spring_batis /pom.xml <?xml version="1.0" encodin ...
- 怎么快速入门一个老的java项目
作者:eilen著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 1.有文档肯定先看文档,先看设计文档,产品的.技术的设计文档,接口文档写的好的可以看看,要是写的不好不着急看. 2 ...
- 阿里云Centos等更新源设置成阿里源方法。
https://help.aliyun.com/knowledge_detail/5974184.html ---------------------------------------------- ...