倍增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\) 条路径,每条路径有 ...
随机推荐
- 数据结构&字符串:字典树
前缀树里面可以存一堆字符串,也可以说是一堆单词,存完之后我们可以轻松判断一个指定的字符串是否出现过 下面我来详细解释一下实现细节 *+; //单词个数*每一个单词的字符数 ; struct Trie ...
- [洛谷P1822] 魔法指纹
洛谷题目连接:魔法指纹 题目描述 对于任意一个至少两位的正整数n,按如下方式定义magic(n):将n按十进制顺序写下来,依次对相邻两个数写下差的绝对值.这样,得到了一个新数,去掉前导0,则定义为ma ...
- PHP系统编程--01.多进程与多线程
PHP中提供了一个扩展pcntl,可以利用操作系统的fork调用来实现多进程.fork调用后执行的代码将是并行的. PHP官方没有提供多线程的扩展,pecl中有一个pthreads扩展提供了多线程的特 ...
- iOS Button设置
UIButton *kefuBtn = [[UIButton alloc]initWithFrame:CGRectMake(, , , )]; kefuBtn.backgroundColor = SX ...
- Python遍历文件夹和读写文件的方法
需 求 分 析 1.读取指定目录下的所有文件2.读取指定文件,输出文件内容3.创建一个文件并保存到指定目录 实 现 过 程 Python写代码简洁高效,实现以上功能仅用了40行左右的代码~ 昨天用Ja ...
- 51nod 1106 质数检测——Mr判素数
质数检测一般都是根号n的写法 当然Mr判素数的方法可以实现log的复杂度2333 Mr判素数的话 我们根据费马小定理只要P是素数 那么另一个素数x 满足 x^P-1≡1(mod P) 同时 x^2%P ...
- Spring4+SpringMVC+MyBatis整合思路(山东数漫江湖)
1.Spring框架的搭建 这个很简单,只需要web容器中注册org.springframework.web.context.ContextLoaderListener,并指定spring加载配置文件 ...
- MFC不同工程(解决方案)之间对话框资源的复制与重用方法(转)
原文转自 https://blog.csdn.net/lihui126/article/details/45556687
- 利用Python 发送邮件
概要 我们都知道SMTP(简单邮件传输协议),是一组用于从原地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式.SMTP规定电子邮件应该如何格式化.如何加密,以及如何在邮件服务器之间传递.SMT ...
- EXT入门学习
今天,对EXT做了一下初步的了解,了解了一些基本用的函数.窗体对象.表单.文本域.按钮,一些基本的函数我列了出来,写了个登陆的demo,是根据别人的例子模仿出来的,见谅哈. 基本函数 Ext.onRe ...