【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!!
根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所以,我们可以用遍历路径的方法求 LCA。
但想想都知道啦,这种遍历的方法肯定too slow,最坏情况时可达到O(n),数据大点儿,就光荣TLE了。
所以我们高级的化身——倍增算法就出现了!
谈谈倍增——
倍增简单来讲就是两个点跳到同一高度后,再一起往上跳,直到跳到一个共同的点,就能找到它们的最近公共祖先啦
具体来说,分为以下几个步骤——
首先,我们得找几个帮手——
| 数组 | 解释 |
| f[][] | 预处理倍增f[v][i] |
| d[][] | 记录节点深度 |
| vis[][] | 判重,以防搜到节点的爸爸 |
| head[][] | 存图时用der~(不懂戳) |
| dis[][] |
一个节点到根的最短距离 (有权值记得加权值) |
Step1: 预处理每个结点的深度和该节点的父亲节点
我们用 d 数组来表示每个结点的深度,设节点 1 为根,d[1]=0,初始化 f[v][0]=u, 表示 v 的 20 的祖先节点为 u。
tips:如果树是无根树时需要把它变成有根数(随便找个点就OK)
Step2: 预处理倍增f[v][i],2j 的祖先也是该点 2j-1 祖先的祖先
核心: f[u][j] = f [f [u][j-1]][j-1]
不懂的盆友,我们来举个例子——

我们有这样的一张图,我们对它进行预处理就会变成这样——

*图源戳
还不懂的,就手动模拟一波~
Step3: 查询时,深度大的先往上跳,直至与深度小的点再同一层。若此时两节点相同直接返回此节点(在同一条链上),如果不同,就利用倍增的思想,同时让 x 和 y 向上找,直到找到深度相等且最小的 x 和 y 的祖先 x′,y′,满足 x′≠y′。此时他们的父结点即为 x 和 y 的最近公共祖先 LCA。
倍增解法是 LCA 问题的在线解法,整体时间复杂度是 O(VlogV+QlogV),其 中 Q 是总查询次数。
看起来是不是还不错?那我们来看道题吧~
谈谈题目——
Description
给定一棵n个点的树,Q个询问,每次询问点x到点y两点之间的距离。
Input
第一行一个正整数n,表示这棵树有n个节点;接下来n-1行,每行两个整数x,y表示x,y之间有一条连边;然后一个整数Q,表示有Q个询问;接下来Q行每行两个整数x,y表示询问x到y的距离。
对于全部数据,1≤n≤105,1≤x,y≤n。
Output
输出Q行,每行表示每次询问的答案。
Example
stdin
6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
stdout
3
4
*原题戳
Thinking
说实话这题没什么好讲的叭,就是求LCA就OK啦,板子题耶!
上代码——
#include<bits/stdc++.h>
using namespace std;
int t,q,tot;
int dis[];
int vis[];
struct node{
int nxt,to;
}e[];
int head[];
int f[][];
int d[]; void add(int u, int v){
e[++tot].to = v;
e[tot].nxt=head[u];
head[u] = tot;
} void dfs(int u, int fa, int dep){ //预处理深度,倍增数组
vis[u] = ;
f[u][] = fa; //初始化记录父节点
d[u] = dep;
for(int j = ; j <= ; j++) f[u][j] = f[f[u][j-]][j-];
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(vis[v]) continue;
dfs(v,u,dep+);
}
} int lca(int x, int y){ //倍增求 LCA
if(d[x] > d[y]) swap(x,y);
for(int i = ; i >= ; i--){
if(d[f[y][i]] >= d[x]) y = f[y][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(){
scanf("%d",&t);
tot = ;
for(int i = ; i < t; i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(,,);
cin >> q;
for(int i = ; i <= q; i++){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",d[x]+d[y]-*d[lca(x,y)]); //两个节点到跟的距离减去重复计算的它们公共祖先到跟的距离
}
}
解释一下这个东西 d[x] + d[y] - 2 * d[lca(x,y)];
画张图——

d[x] 和 d[y] 就是红色那两条东西
d[lca(x,y)] 就是蓝色那条
d[x] + d[y] - 2*d[lca(x,y)] 就是绿色那条啦~
当然这是没有权值得时候,我们默认深度差不多等于距离,但有了权值就不一样了。
我们再来看一道板子题——
Description
给出n个点的一棵树,多次询问两点之间的最短距离。
注意:边是双向的。
Input
第一行为两个整数n和m。n表示点数,m表示询问次数; 下来n-1行,每行三个整数x,y,k,表示点x和点y之间存在一条边长度为k;在接下来m行,每行两个整数x,y,表示询问点x到点y的最短距离。
对于全部数据,2≤n≤104,1≤m≤2×104,0<k≤100,1≤x,y≤n。
Output
输出m行。对于每次询问,输出一行。
Example
stdin1
2 2 1 2 100 1 2 2 1
stdout1
100 100
stdin2
3 2 1 2 10 3 1 15 1 2 3 2
stdout2
10 25
*原题戳
#include<bits/stdc++.h>
using namespace std;
int n,m,q,tot;
int dis[];
int vis[];
struct node{
int nxt,to;
int w;
}e[];
int head[];
int f[][];
int d[]; void add(int u, int v, int w){
e[++tot].to = v;
e[tot].w = w;
e[tot].nxt=head[u];
head[u] = tot;
} void dfs(int u, int fa, int dep){
vis[u] = ;
f[u][] = fa;
d[u] = dep;
for(int j = ; j <= ; j++) f[u][j] = f[f[u][j-]][j-];
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(vis[v]) continue;
dis[v] = dis[u] + e[i].w;
dfs(v,u,dep+);
}
} int lca(int x, int y){
if(d[x] > d[y]) swap(x,y);
for(int i = ; i >= ; i--){
if(d[f[y][i]] >= d[x]) y = f[y][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(){
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(,,);
for(int i = ; i <= m; i++){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",dis[x]+dis[y]-*dis[lca(x,y)]);
}
}
注意到没有?
这一道题是有权值的,所以最后输出的时候输出的是 dis[x] + dis[y] - 2 * dis[lca(x,y)]
总结一下
其实LCA还有别的不同的求法,下次在和你们讲吧(其实是我还没学会)
这次就先到这儿吧~
拜拜~
(如果文章有不对的地方,请指出,谢谢啦^=^)
【lhyaaa】最近公共祖先LCA——倍增!!!的更多相关文章
- 最近公共祖先 LCA 倍增算法
树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...
- luogu3379 【模板】最近公共祖先(LCA) 倍增法
题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...
- 最近公共祖先 LCA 倍增法
[简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- suoi31 最近公共祖先2 (倍增lca)
根为r时x.y的公共祖先,就是lca(x,r),lca(x,y),lca(r,y)中深度最大的那一个,不要再在倍增的时候判来判去还判不对了... #include<bits/stdc++.h&g ...
- [luogu3379]最近公共祖先(树上倍增求LCA)
题意:求最近公共祖先. 解题关键:三种方法,1.st表 2.倍增法 3.tarjan 此次使用倍增模板(最好采用第一种,第二种纯粹是习惯) #include<cstdio> #includ ...
随机推荐
- Mysql----左连接、右连接、内连接、全连接的区别
最近,突然想起来数据库有好些时间没用到,所以,想把数据库有关的知识回顾一下,所以接下来这个月,基本上会以数据库的帖子来写为主,首先,很多同学都会有个错觉,觉得学习数据库会sql语句的增删改查就够了,其 ...
- 使用pycharm、跳板机连接内网服务器
使用pycharm.跳板机连接内网服务器 接手实验室服务器后,大部分同学在GPU集群上跑程序都是直接在ssh界面上跑,这里想着通过pycharm通过跳板机来连接服务器. 总体就是实验室服务器仅限内网访 ...
- 用Vue实现一个简单的图片轮播
本文已收录至https://github.com/likekk/studyBlog欢迎大家star,共同学习,共同进步.如果文章有错误的地方,欢迎大家指出.后期将在将GitHub上规划前端学习的路线和 ...
- svg 使用中的疑惑点
svg,g ,defs,symbol 都是容器元素,使用起来给人许多疑惑 svg-spirite-loader在页面生成的svg标签有什么特点? svg标签里面的symbol有什么用? 这些标签能够随 ...
- 给网站接入CloudFlare的CDN
注册并登录咱的CF账号 emmmmm 添加咱的域名 食用DNS
- Redis(二)Jedis操作Redis
如果测试连接的时候,报下面这个异常,可以参考下面的博客进行处理: Exception in thread "main" redis.clients.jedis.exceptions ...
- spring学习(八)事务操作
一.事务的概念: 事务是并发控制的单位,一系列操作组成的工作单元,该工作单元内的操作是不可分割的,也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做,所有操作必须成功完成,否则 ...
- ajax快速入门
一.ajax简单入门 1.Ajax的实现步骤 // 1.创建ajax对象var xhr = new XMLHttpRequest();// 2.高数ajax请求地址及请求方式//第一个参数就是请求方式 ...
- Learning in the Frequency Domain 解读
论文:Learning in the Frequency Domain, CVPR 2020 代码:https://github.com/calmevtime/DCTNet 实际的图像尺寸比较大,无法 ...
- 简单理解:数据库的一致性与四种隔离级别(+MySQL实现)
并行数据库存在着几种常见不一致问题: 1.更新丢失:两个并发的写进程同时修改某内容,一个没修改完提交之后另一个又提交,导致其覆盖了第一个提交的写进程内容. 2.脏读:一个操作读到了另外一个操作没有提交 ...