倍增法求LCA(最近公共最先)
对于有根树T的两个结点u、v,最近公共祖先x=LCA(u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
如图,根据定义可以看出14和15的最近公共祖先是10, 15和16的最近公共祖先是1, 6和5的最近公共祖先是5......
假如我要求14和16的最近公共祖先,要怎么做呢?
最暴力的做法,就是先看14和16在不在同一层,如果他们不在同一层,那么较深的那个点往上爬(即距离根较远的那个点)
一直爬,爬到两点的深度一样
当两点深度一样时,判断他们是否在同一个点上,如果不是,则两个点同时往上爬,直到这两个点是同一个点
显然,这么做一般来说都是不行的。
下面介绍倍增法(图是用vector存的)
既然一步一步往上爬太慢了,那就一次爬远一点
我们预处理一个数组fa【x】【i】使游标快速移动,大幅度减少跳转次数。
fa【x】【i】表示 点x的第2^i个祖先
拿上图来说,fa【15】【0】就是点15的第2^0个祖先,即12(fa【15】【0】=12)
fa【15】【1】就是点15的第2^1个祖先,即10(fa【15】【1】=10)
fa【15】【2】就是点15的第2^2个祖先,即5(fa【15】【2】=5)
怎么预处理出这个数组呢?我们观察上图,15的第一个祖先是12,12的第一个祖先是10
即fa【15】【0】=12 fa【12】【0】=10
而15的第二个祖先是10,和12的第一个祖先是一样的
fa【15】【1】=10
也就是说,对于点15来说,15的第二个祖先就是15的第一个祖先的第一个祖先(点12的第一个祖先)
用数组表示是fa[15][1]=fa[ fa[15][0] ][ 0 ];
即fa[15][1]=fa[12][0]
推广到第2^i个祖先:递推式:fa[rt][i]=fa[fa[rt][i-1]][i-1];(rt为当前节点)
预处理代码:(depth数组存的是当前节点的深度)
void dfs(int f,int rt )//rt是当前节点,f是当前节点的父亲
{
depth[rt]=depth[f]+;
fa[rt][]=f;
for(int i=;i<;++i)//自己估计i的范围
fa[rt][i]=fa[fa[rt][i-]][i-];
for(int i=;i<E[rt].size();++i)//用vector存的图,遍历当前节点的每一个孩子
dfs(rt,E[rt][i]);
}
下一个问题是,游标移动到哪里合适?
我们的原则是,先让两个点的深度一致
然后,让两个点同时跳相同的步数
1.如果跳出根以外了,就不跳
2.如果跳到的点是相同的点,也不跳(不一定是最近的公共祖先)
不是以上两种情况,就跳
举个栗子,如图:
假如我们问14和15的lca是哪个点
按照规则
我们先让14和15在同一层上(深度一致)
14跳到了13
然后,我们看跳多少步合适,如果i=3,即2^3=8步,发现已经跳出根以外了,不跳
再看i=2, 2^2=4,13和15的第四个祖先都是5,不跳
再看i=1,即跳2步,发现都是10,不跳
再看i=0,跳一步,13跳一步到11,15跳一步到12,两个点不相同,可以跳
此时第一遍循环结束,判断这两个点的第一个祖先是不是同一个点
如果是,则找到了
如果不是,那就继续进行循环
用图表示:
第一步是14跳到13
第二步是13跳到11,15跳到12
发现11和12的第一个祖先都是10,找到答案,结束循环 (时间是log级别的)
代码:
int lca(int x,int y)//找x和y的最近公共祖先
{
if(depth[x]<depth[y])//调整x的深度大一些
swap(x,y);
while(depth[x]!=depth[y])//使x和y的深度一致
{
for(int i=;i>=;--i){//初始常数自己根据问题进行调整
if(depth[x]-(<<i)>=depth[y])
x=fa[x][i];
}
}
if(x==y) return x;
while(fa[x][]!=fa[y][])//当x和y的第一位祖先都一样时退出循环
{
for(int i=;i>=;--i){
//如果没有出界而且两点的祖先不一样,就跳
if(fa[x][i]!=&&fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
}
}
return fa[x][];
}
代码自己写的,有点丑,网上的没看懂....
下面给出测试代码和样例,大家可以去试一试,样例就是第一幅图
#include <iostream>
#include <queue>
#include <vector>
#include <stdio.h>
using namespace std;
vector<int> E[];
int depth[];
int fa[][];
void dfs(int f,int rt )
{
depth[rt]=depth[f]+;
fa[rt][]=f;
for(int i=;i<;++i)
fa[rt][i]=fa[fa[rt][i-]][i-];
for(int i=;i<E[rt].size();++i)
dfs(rt,E[rt][i]);
}
int lca(int x,int y)
{
if(depth[x]<depth[y])//调整左边深
swap(x,y);
while(depth[x]!=depth[y])
{
for(int i=;i>=;--i){
if(depth[x]-(<<i)>=depth[y])
x=fa[x][i];
}
}
if(x==y) return x;
while(fa[x][]!=fa[y][])
{
for(int i=;i>=;--i){
if(fa[x][i]!=&&fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
}
}
return fa[x][];
}
int main()
{
int l,r;
for(int i=;i<=;++i){
cin>>l>>r;
E[l].push_back(r);
}
dfs(,);
int a,b,t=;
while(t--)
{
cin>>a>>b;
cout<<lca(a,b)<<endl;
} return ;
}
/*
输入图:
1 2
2 4
4 5
5 6
5 7
7 10
10 11
10 12
11 13
12 15
13 14
1 3
3 8
8 9
9 16
*/
倍增法求LCA(最近公共最先)的更多相关文章
- 倍增法求lca(最近公共祖先)
倍增法求lca(最近公共祖先) 基本上每篇博客都会有参考文章,一是弥补不足,二是这本身也是我学习过程中找到的觉得好的资料 思路: 大致上算法的思路是这样发展来的. 想到求两个结点的最小公共祖先,我们可 ...
- 倍增法求LCA
倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可 ...
- HDU 2586 倍增法求lca
How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- 树上倍增法求LCA
我们找的是任意两个结点的最近公共祖先, 那么我们可以考虑这么两种种情况: 1.两结点的深度相同. 2.两结点深度不同. 第一步都要转化为情况1,这种可处理的情况. 先不考虑其他, 我们思考这么一个问题 ...
- 在线倍增法求LCA专题
1.cojs 186. [USACO Oct08] 牧场旅行 ★★ 输入文件:pwalk.in 输出文件:pwalk.out 简单对比时间限制:1 s 内存限制:128 MB n个被自 ...
- 倍增法求lca:暗的连锁
https://loj.ac/problem/10131 #include<bits/stdc++.h> using namespace std; struct node{ int to, ...
- 倍增法求LCA代码加详细注释
#include <iostream> #include <vector> #include <algorithm> #define MAXN 100 //2^MA ...
- 浅谈倍增法求解LCA
Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...
- RMQ(倍增法求ST)
解决什么问题:区间查询最值 倍增思想:每次得出结果的范围呈2的幂次增长,有人说相当于二分,目前我觉得相当于线段树的查找. 具体理解看代码: /*倍增法求ST*/ #include<math.h& ...
随机推荐
- Java -- 基于JDK1.8的LinkedList源码分析
1,上周末我们一起分析了ArrayList的源码并进行了一些总结,因为最近在看Collection这一块的东西,下面的图也是大致的总结了Collection里面重要的接口和类,如果没有意外的话后面基本 ...
- mysql服务启动不了 More help is available by typing NET HELPMSG 3534
解决方法:参考 注意:如果安装MySQL的时候改了端口(如从3306改成3307),则my.ini配置文件的端口也需要相应的修改
- 关于C++ return * this
转自 :https://blog.csdn.net/u011846436/article/details/45222905 不废话,直接上例子,使用赋值构造函数解释为什么需要 return *this ...
- 【转】Powershell与jenkins集成部署的运用(powershell运用)
powershell简介: 远程管理采用的一种新的通信协议,Web Services for Management,简称WS-MAN它通过http或者https进行工作,WS-WAN的实现主要基于一个 ...
- pickel加速caffe读图
64*64*3小图(12KB),batchSize=128,训练样本100万, 全部load进来内存受不了,load一次需要大半天 训练时读入一个batch,ali云服务器上每个batch读入时间1. ...
- WebStorm过期解决方法
第一步:先删除C:\Users\本机用户名\.WebStorm2016.3\config文件中eval文件. 第二步:打开webstrom 如图,填写许可证激活码:http://idea.imsxm. ...
- TeamCity 创建docker构建步骤
1 dockerfile source 选择dockerfile文件的路径,一共有三种方式: 1.1.1 file content 这种方式是在线写dockerfile文件. 其在进行创建的时候会在 ...
- op 和 oo 的区别
本是之前一位前辈留下的问题,因为我不是程序出身,略懂一些代码,后又查了很多人的博客,问了周围搞开发的朋友,得出以下结论: 有人这么形容OP和OO的不同:用面向过程的方法写出来的程序是一份蛋炒饭,而用面 ...
- Q语言-[帝王三国送将辅助]
纯属自己写的, 玩同一个游戏的朋友,需要送将的, 把需要送的将改名为送, 然后启动辅助即可 本辅助只支持1024x576 191dpi 附上源码 //本源码初始化分辨率1024x576[夏天] Dim ...
- Codeforces 101623E English Restaurant - 动态规划
题目传送门 传送门 题目大意 餐厅有$n$张桌子,第$i$张桌子可以容纳$c_i$个人,有$t$组客人,每组客人的人数等概率是$[1, g]$中的整数. 每来一组人数为$x$客人,餐厅如果能找到最小的 ...