以下转自: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. (转)Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File (三) —— SharePreferences

    除了SQLite数据库外,SharedPreferences也是一种轻型的数据存储方式,它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息.其存储位置在/data ...

  2. 【最小割/二分图最大独立集】【网络流24题】【P2774】 方格取数问题

    Description 给定一个 \(n~\times~m\) 的矩阵,每个位置有一个正整数,选择一些互不相邻的数,最大化权值和 Limitation \(1~\leq~n,~m~\leq~100\) ...

  3. 21天实战caffe笔记_第二天

    1 传统机器学习 传统机器学习:通过人工设计特征提取器,将原始数据转化为合适的中间表示形式或者特征向量,利用学习系统(通常为分类器)可以对输入模式进行检测或者分类.流程如下: 传统机器学习的局限在于需 ...

  4. Chapter10(泛型算法)--C++Prime笔记

    关键:算法通过在迭代器上进行操作来实现类型无关.算法不改变所操作序列的大小. 1.算法大多都定义在algorithm头文件中,标准库还在头文件numeric中定义了一组数值泛型算法. 2.泛型算法永远 ...

  5. bzoj 1193 贪心+bfs

    1193: [HNOI2006]马步距离 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2015  Solved: 914[Submit][Statu ...

  6. Java入门:MyEclipse安装与破解教程

    MyEclipse Pro 2014 GA的安装过程请参考网页:http://blog.my-eclipse.cn/myeclipse-2014-download-and-install.html 安 ...

  7. P2627 修剪草坪

    P2627 修剪草坪 题目描述 在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪.现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠. 然而,Fa ...

  8. caffe 配置文件详解

    主要是遇坑了,要记录一下. solver算是caffe的核心的核心,它协调着整个模型的运作.caffe程序运行必带的一个参数就是solver配置文件.运行代码一般为 # caffe train --s ...

  9. Java入门系列(三)面向对象三大特性之封装、继承、多态

    面向对象综述 封装 封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口. 有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者:而外部调用者也可以知道 ...

  10. Python入门系列教程(四)字典

    既能存储多个数据,还能在访问元素的很方便就能够定位到需要的那个元素 增 Dic = {'name':'沐风', 'city':'北京'} Dic['sex']='男' print Dic 删 del ...