倍增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\) 条路径,每条路径有 ...
随机推荐
- codevs 1332 上白泽慧音
1332 上白泽慧音 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大 ...
- CentOS7安装Memcached 三步曲
1.yum 安装 yum clean allyum -y updateyum -y install memcached 2.Memcached 运行 memcached -h //查看考号修改配置vi ...
- Quick-Cocos2dx-Community_3.6.3_Release 编译时libtiff.lib 无法解析
Quick-Cocos2dx-Community_3.6.3_Release 使用VS2012编译,报错: libtiff.lib lnk2001 无法解析的外部符号 ltod3 类似于上面这种,刚才 ...
- (转)matlab练习程序(HOG方向梯度直方图)
matlab练习程序(HOG方向梯度直方图)http://www.cnblogs.com/tiandsp/archive/2013/05/24/3097503.html HOG(Histogram o ...
- Commonjs,AMD,CMD和UMD的差异
CommonJS 一种服务器端模块化的规范,Nodejs实现了这种规范,所以就说Nodejs支持CommonJS. CommonJS分为三部分: require 模块加载 exports 模块导出 m ...
- js删除数组中重复的元素
1.方法一 将数组逐个搬到另一个数组中,当遇到重复元素时,不移动,若元素不重复则移动到新数组中 function unique(arr){ var len = arr.length; var resu ...
- vue中的图片加载与显示默认图片
HTML: <div class="content-show-img"> <div class="show-img"> <img ...
- Python模块学习 - ConfigParser
配置文件 很多软件都用到了配置文件,像git运行的时候会读取~/gitconfig,MySQL运行的时候会读取/etc/my.cnf,Python 提供的包管理工具pip命令,也会去读取~/.pip/ ...
- python基础===装饰器@property 的扩展
以下来自Python 3.6.0 Document: class property(fget=None, fset=None, fdel=None, doc=None) Return a proper ...
- MS SQLServer 批量附加数据库
/************************************************************ * 标题:MS SQLServer 批量附加数据库 * 说明:请根据下面的注 ...