用“倍增法”求最近公共祖先(LCA)
1.最近公共祖先:对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、的祖先且x的深度尽可能大。
2.朴素算法:记录下每个节点的父亲,使节点u,v一步一步地向上找父亲,直到找到相同的“祖先”,即是所求的答案,时间复杂度O(n)。
3.优化算法(倍增法):利用二进制的思想,想办法使一步一步向上搜变成以2^k地向上跳。所以定义一个P[][]数组,使p[i][j]表示节点i的2^j倍祖先,因此p[i][0]即为i的父亲。我们可以得到一个递推式p[i][j]=p[p[i][j-1]][j-1]。这样子一个O(NlogN)的预处理(dfs)的 2^k 的祖先。定义一个deep[]数组表示节点深度,先判断是否 deep[u] > deep[v]果是的话就交换一下(保证 u的深度小于 v方便下面的操作)然后把u到与v同深度,同深度以后再把u v同时往上调(dec(j)) 调到有一个最小的j 满足: p[u] [j]!=p[v][j],u,v是在不断更新的 最后把u,v 往上调 (u=p[u,0] v=p [v,0]) 一个一个向上调直到 u= v 这时 u or v就是公共祖先。复杂度:O(logn)
下面给出 LCA 的模板:
输入:第一行:N,M,Q (因为是一棵树,所以M==N-1)
接下来M 行: u, v, c ,表示u到v连一条权值为c的边
接下来Q行:u, v 表示寻求u,v的最近公共祖先,u~v的距离,u~v之间的路径的最大权值
输出:共Q行,对应上述的询问
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=;
inline int read(){
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int N,M,Q;
vector<int> to[maxn],cost[maxn];
int p[maxn][],MAX[maxn][],sum[maxn][];
int dep[maxn];
inline void dfs(int root){
for(int i=;i<to[root].size();i++){
int y=to[root][i];
if(y!=p[root][]){
dep[y]=dep[root]+;
p[y][]=root;
MAX[y][]=cost[root][i];
sum[y][]=cost[root][i];
for(int k=;k<=30;k++){
int zu=<<k;
if(zu<=dep[y]){
p[y][k]=p[p[y][k-]][k-];
MAX[y][k]=max(MAX[y][k-],MAX[p[y][k-]][k-]);
sum[y][k]=sum[y][k-]+sum[p[y][k-]][k-];
}
}
dfs(y);
}
}
}
inline void LCA(int x,int y){
int ans1=,ans2=;
if(dep[x]>dep[y]) swap(x,y);
int delta=dep[y]-dep[x];
for(int i=;i<=;i++){
int h=<<i; h=hδ
if(h!=){
ans1+=sum[y][i]; ans2=max(ans2,MAX[y][i]);
y=p[y][i];
}
}
if(x==y){
cout<<x<<" "<<ans1<<" "<<ans2<<endl;
return ;
}
for(int i=;i>=;i--){
if(p[y][i]!=p[x][i]){
ans1+=sum[x][i]; ans1+=sum[y][i];
ans2=max(ans2,MAX[x][i]); ans2=max(ans2,MAX[y][i]);
x=p[x][i]; y=p[y][i];
}
}
ans1+=sum[x][]; ans1+=sum[y][];
ans2=max(ans2,MAX[x][]); ans2=max(ans2,MAX[y][]);
cout<<p[x][]<<" "<<ans1<<" "<<ans2<<endl;
}
int main(){
N=read(); M=read(); Q=read();
for(int i=;i<=M;i++){
int u,v,c;
u=read(); v=read(); c=read();
to[u].push_back(v); to[v].push_back(u);
cost[u].push_back(c); cost[v].push_back(c);
}
p[][]=-; dep[]=;
dfs();
for(int i=;i<=Q;i++){
int u,v;
u=read(); v=read();
LCA(u,v);
}
return ;
}
用“倍增法”求最近公共祖先(LCA)的更多相关文章
- LCA 在线倍增法 求最近公共祖先
第一步:建树 这个就不说了 第二部:分为两步 分别是深度预处理和祖先DP预处理 DP预处理: int i,j; ;(<<j)<n;j++) ;i<n;++i) ) fa[i ...
- 求最近公共祖先(LCA)的各种算法
水一发题解. 我只是想存一下树剖LCA的代码...... 以洛谷上的这个模板为例:P3379 [模板]最近公共祖先(LCA) 1.朴素LCA 就像做模拟题一样,先dfs找到基本信息:每个节点的父亲.深 ...
- 倍增法求lca(最近公共祖先)
倍增法求lca(最近公共祖先) 基本上每篇博客都会有参考文章,一是弥补不足,二是这本身也是我学习过程中找到的觉得好的资料 思路: 大致上算法的思路是这样发展来的. 想到求两个结点的最小公共祖先,我们可 ...
- 最近公共祖先 LCA 倍增算法
树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...
- 倍增法求LCA
倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可 ...
- 【LCA求最近公共祖先+vector构图】Distance Queries
Distance Queries 时间限制: 1 Sec 内存限制: 128 MB 题目描述 约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道.因此他决心找一条更合理的赛道 ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- HDU 2586 倍增法求lca
How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- 最近公共祖先(LCA)的三种求解方法
转载来自:https://blog.andrewei.info/2015/10/08/e6-9c-80-e8-bf-91-e5-85-ac-e5-85-b1-e7-a5-96-e5-85-88lca- ...
随机推荐
- Windows下MySQL配置及安全加固总结
Windows下MySQL配置及安全加固总结 在网管的实际使用过程中,MySQL数据库在安装后的配置及安全加固内容,在客户中逐渐要求越来越高.从反馈的问题看,一般都是由第三方软件公司的软件扫描整个系统 ...
- 面试心得与总结—BAT、网易、蘑菇街 - ImportNew
1. 九种基本数据类型的大小,以及他们的封装类. 2. Switch能否用string做参数? 3. equals与==的区别. 4. Object有哪些公用方法? 5. Java的四种引用,强弱软虚 ...
- FineReport----查询功能 的知识点
1.设置日期控件,默认当前日期 2.默认不查询 选择参数:点击查询前不显示报表内容
- Oracle的服务介绍以及正常运行必须启动的服务
成功安装Oracle 11g数据库后,你会发现自己电脑运行速度会变慢,配置较低的电脑甚至出现非常卡的状况,通过禁止非必须开启的Oracle服务可以提升电脑的运行速度.那么,具体该怎么做呢? 按照win ...
- pro_update_role_pwd
DELIMITER | drop procedure if exists pro_update_role_pwd; CREATE PROCEDURE pro_update_role_pwd ( cro ...
- windows 全角 怎么切换到半角
windows 全角 怎么切换到半角 :shift+空格键
- 关于vtt 与 srt 字幕 的相互转换
我在下载的udacity中教程时,字幕和视频是分离的,对于英文还无法完全听懂的我来说,字幕还是比较重要.不想看解释的可直接跳到最后复制代码运行即可. 查看了vtt和srt的区别,使用记事本打开vtt和 ...
- JavaScript 入门之常见对象
常见对象 1. Object 对象 2. String 对象 3. Array 对象 4. Date 对象 5. Number 对象 6. 自定义对象 with 语句 为了简化对象调用内容的书写 格式 ...
- 如何在 window 上面输入特殊字符?
打开 字符映射表 程序 选中任意一个字符,它会在下方显示该字符的 16进制 转换16进制至10进制,并在输入法打开的状态下,按住 Alt 键输入 10 进制数值即可.
- Django框架视图类
类视图 在写视图的时候,Django除了使用函数作为视图,也可以使用类作为视图.使用类视图可以使用类的一些特性,比如继承等. View django.views.generic.base.View是主 ...