以下转自:https://www.cnblogs.com/JVxie/p/4854719.html

首先是最近公共祖先的概念(什么是最近公共祖先?):

在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大公共祖先节点

换句话说,就是两个点在这棵树上距离最近的公共祖先节点

所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。

有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢?

答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而LCA还可以将自己视为祖先节点

举个例子吧,如下图所示最近公共祖先是2最近公共祖先最近公共祖先。 

这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近公共祖先呢?

通常初学者都会想到最简单粗暴的一个办法:对于每个询问,遍历所有的点,时间复杂度为O(n*q),很明显,n和q一般不会很小

常用的求LCA的算法有:Tarjan(离线)、倍增法(在线)

下面贴两种写法的代码,以HDU 2586 How far away ?为例:

题目大意:

给你一棵树n个节点,m次询问,每次询问树上节点u和v的最小距离。

Tarjan(时间复杂度O(n+Qlogn))

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=4e4+; struct node{
int to,w;
node(int to,int w):to(to),w(w){}
}; struct qnode{
int to,id;
qnode(int to,int id):to(to),id(id){}
}; vector<node>v[N];
vector<qnode>q[N];
bool vis[N]; //vis[i]表示点i是否被访问过
int res[N],root[N],dis[N]; //res记录答案 int find(int x){
return root[x]==x?x:root[x]=find(root[x]);
} void lca(int u){
vis[u]=true;
for(int i=;i<v[u].size();i++){
node t=v[u][i];
if(!vis[t.to]){
dis[t.to]=dis[u]+t.w;
lca(t.to);
root[t.to]=u;
}
}
for(int i=;i<q[u].size();i++){
qnode t=q[u][i];
if(vis[t.to]){
res[t.id]=dis[u]+dis[t.to]-*dis[find(t.to)];
}
}
} int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
memset(vis,false,sizeof(vis));
memset(dis,,sizeof(dis));
for(int i=;i<=n;i++){
root[i]=i;
v[i].clear();
}
for(int i=;i<=m;i++){
q[i].clear();
}
for(int i=;i<=n-;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
v[a].push_back(node(b,c));
v[b].push_back(node(a,c));
}
for(int i=;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
q[a].push_back(qnode(b,i));
q[b].push_back(qnode(a,i));
}
lca(1);
for(int i=;i<=m;i++){
printf("%d\n",res[i]);
}
}
return ;
}

倍增法(时间复杂度O(nlogn+Qlogn))

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e5+; struct node{
int to,w;
node(int to,int w):to(to),w(w){}
};
vector<node>v[N]; int n,m;
int depth[N],fa[N][],dis[N];
bool vis[N]; void init(){
memset(depth,,sizeof(depth));
memset(fa,,sizeof(fa));
memset(dis,,sizeof(dis));
memset(vis,false,sizeof(vis));
for(int i=;i<=n;i++) v[i].clear();
} void dfs(int u){
vis[u]=true;
for(int i=;i<v[u].size();i++){
node t=v[u][i];
if(!vis[t.to]){
depth[t.to]=depth[u]+;
fa[t.to][]=u;
dis[t.to]=dis[u]+t.w;
dfs(t.to);
}
}
} //倍增,处理fa数组
void bz(){
for(int j=;j<=;j++){
for(int i=;i<=n;i++){
fa[i][j]=fa[fa[i][j-]][j-];
}
}
} int lca(int x,int y){
//保证深度大的点为x
if(depth[x]<depth[y])
swap(x,y);
int dc=depth[x]-depth[y];
for(int i=;i<;i++){
if(<<i&dc) //一个判断,模拟下就会清楚
x=fa[x][i];
}
if(x==y) return x; //如果深度一样,两个点相同,直接返回
for(int i=;i>=;i--){
if(fa[x][i]!=fa[y][i]){ //跳2^i不一样,就跳,否则不跳
x=fa[x][i];
y=fa[y][i];
}
}
x=fa[x][];
return x;
} int main(){
int t;
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&m);
for(int i=;i<=n-;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
v[a].push_back(node(b,c));
v[b].push_back(node(a,c));
}
dfs();
bz();
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)]);
}
}
return ;
}

相关题目了解一下(转自这里):

hdu 2586 How far away ?

模板题,直接求LCA,可以在线,离线算法均可解,可以测试一下模板

poj 1986 Distance Queries

模板题,直接求LCA

hdu 2874 Connections between cities

模板题,不过不是树是森林,所以某些点不存在LCA,要做判断

zoj 3195 Design the city

任然算是模板题,上面的题要求两点间的最短距离,这里要求3点间的最短距离,其实就是两两之间求一次LCA并计算出距离,然后相加除以2即可

hdu 3078 Network

LCA + 修改点权值 + 排序:每个点有初始的权值,一边查询一边伴随着修改某些点的权值,查询是从a到b路径中第k大的权值是多少。不需要太多的技巧,修改操作就直接修改,查询操作先求LCA,然后从a走到b保存下所有的权值,排序,然后直接输出即可

poj 2763 Housewife Wind

LCA + 修改边权:一边查询两点间的距离,一边修改某些边权。对于修改了某些边的边权,就要从此开始遍历下面的子孙后代更改他们的dir值(点到根的距离)。也不需要太多技巧,直接按题意实现即可,不过时间比较糟糕,用线段树或树状数组可以对修改操作进行优化,时间提升很多

poj 3694 Network

连通分量 + LCA : 先缩点,再求LCA,并且不断更新,这题用了朴素方法来找LCA,并且在路径上做了一些操作

poj 3417 Network

LCA + Tree DP  :  在运行Tarjan处理完所有的LCA询问后,进行一次树DP,树DP不难,但是要想到用树DP并和这题结合还是有点难度

poj 3728 The merchant

LCA + 并查集的变形,优化:好题,难题,思维和代码实现都很有难度,需要很好地理解Tarjan算法中并查集的本质然后灵活变形,需要记录很多信息(有点dp的感觉)

hdu 3830 Checkers

LCA + 二分:好题,有一定思维难度。先建立出二叉树模型,然后将要查询的两个点调整到深度一致,然后二分LCA所在的深度,然后检验

最近公共祖先(LCA)模板的更多相关文章

  1. 最近公共祖先lca模板

    void dfs(int x,int root){//预处理fa和dep数组 fa[x][0]=root; dep[x]=dep[root]+1; for(int i=1;(1<<i)&l ...

  2. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  3. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  4. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  5. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  6. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  7. lca最近公共祖先(模板)

    洛谷上的lca模板题--传送门 学了求lca的tarjan算法(离线),在洛谷上做模板题,结果后三个点超时. 又把询问改成链式前向星,才ok. 这个博客,tarjan分析的很详细. 附代码-- #in ...

  8. LCA最近公共祖先——Tarjan模板

    LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. Tarjan是一种离线算法,时间复杂度O(n+Q),Q表示询问次数,其中 ...

  9. luogu3379 【模板】最近公共祖先(LCA) 倍增法

    题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...

随机推荐

  1. day6-python基础

  2. 洛谷P3201 [HNOI2009]梦幻布丁(链表 + 启发式合并)

    题目链接 给出 \(n\) 个布丁,每个补丁都有其颜色.现在有 \(m\) 次操作,每次操作将第 \(x_i\) 种颜色全部变为第 \(y_i\) 种颜色. 操作中可能会插入询问,回答目前总共有多少段 ...

  3. PyCharm引入自定义类报错

    This inspection detects names that should resolve but don't. Due to dynamic dispatch and duck typing ...

  4. python基础之02列表/元组/字典/set集合

    python中内置的数据类型有列表(list)元组(tuple)字典(directory). 1 list list是一种可变的有序的集合.来看一个list实例: #第一种方法: >>&g ...

  5. 终于把5GB的Cygwin安装完成了

    From:http://www.cnblogs.com/killerlegend/p/3904478.html Author:KillerLegend Date:2014.8.11 首先,只想说一句, ...

  6. LibreOJ#6030. 「雅礼集训 2017 Day1」矩阵

    https://loj.ac/problem/6030 如果矩阵第i列有一个黑色, 那可以用他把第i行全都染黑,也可以使任意一列具有黑色 然后就可以用第i行把矩阵染黑 染黑一列的代价最少是1 染黑一行 ...

  7. PHP7 学习笔记(二)PHP5.9 升级到PHP7 遇到的一些坑的记录(php-fpm 图解)

    apache_event_php-fpm 示意图: nginx-php-fpm示意图: Worker-Master-Server TCP-Nginx_PHP Nginx-FastCGI 1.使用$_G ...

  8. Spring IOC 容器

    <bean name="userBean" class="com.nuts.demo.spring.UserBean"> <property ...

  9. 【AtCoder Grand Contest 001F】Wide Swap [线段树][拓扑]

    Wide Swap Time Limit: 50 Sec  Memory Limit: 512 MB Description Input Output Sample Input 8 3 4 5 7 8 ...

  10. 20155217 2016-2017-2 《Java程序设计》第4周学习总结

    20155217 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 第六章 在java中,继承时使用extends关键字,private成员也会被继承,只不过子 ...