关于LCA:

  LCA 指树上两点的公共祖先。

如何 “暴力” 找两点的 LCA :

  可以先 DFS 一遍求出每个点的 dep (深度)。然后从深度大的点先往上跳,跳到与另一个点相同的深度,如果还没有到达相同的,就两个点一起往上跳,直到达到相同的点,那么,这个点就是两点的 LCA 。

关于倍增法求 LCA :

  其实就是一个经过优化的暴力算法。让两个点一次向上跳多步来优化时间。

如何实现倍增求 LCA :

  设 f [ u ][ k ] 表示 u 的 2k 辈祖先,即从 u 向根节点走 2k 步到达的节点。特别地,若该节点不存在,则令 f [ u ][ k ] = 0 。f [ u ][ 0 ] 就是 x 的父节点。因为 u 向根节点走 2k ⇔ 向根节点走 2k-1 步,再走 2k-1 步。所以对于 k∈ [ 1,logn ] ,有 f [ u ][ k ] = f [ f [ u ][ k-1 ] ][ k-1 ]。

  f 数组利用了递推的思想。递推式为: f [ u ][ k ] = f [ f [ u ][ k-1 ] ][ k-1 ]。因此,我们可以对树进行遍历 DFS ,由此得到 f [ u ][ 0 ],再计算出 f 数组的所有值。(预处理的期望复杂度为 O(nlogn))。

  在预处理完之后可以多次对不同的 x,y 计算 LCA ,每次询问的时间复杂度为 O(logn)。

  基于 f 数组(假装已经预处理)计算 LCA( x, y ) 分为以下几步:

  ①设 dep [ x ] 表示 x 的深度。那么设 dep [ x ] ≥ dep [ y ] 。(否则,可交换 x, y )

  ②利用二进制拆分的思想,把 x 向上调整到与 y 同一的深度。

  即:依次尝试从 x 向上走 k = 2logn… 21,20 步,若到达的点比 y 深,则令 x = f [ x ][ k ]。

  ③若此时的 x = y ,则说明已经找到了 LCA ,两点的 LCA 就等于 y 。

  ④若此时的 x ≠ y ,那么 x, y 同时向上调整,并保持深度一致且二者不会相会。

  具体来说就是,依次尝试把 x, y 同时向上走 k = 2logn… 21,20 步,若 f [ x ][ k ] ≠ f [ y ][ k ](即仍未相会),则令 x = f [ x ][ k ],y = f [ y ][ k ]。

  ⑤此时 x,y 必定只差一步就相会了,他们的父节点 f [ x ][ 0 ] 就是 LCA。

倍增求 LCA 的伪代码:

预处理:

inline void Deal_first(int u,int fa)
{
dep[u]=dep[fa]+;//深度+1
for(int i=;i<=;i++)//2^0 ~ 2^19
f[u][i+]=f[f[u][i]][i];//递推公式,上面讲过了。
for(int i=head[u];i;i<=t[i].nex)//前向星遍历(相当于dfs)
{
int v=t[i].to;//记录子节点
if(v==fa) continue;//防止倒退(因为是无向边)
f[v][]=u;//子节点向上跳一步就是父节点
Deal_first(v,u);//v-子节点,u-父节点
}
}

查询 x,y 的 LCA:

inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);//让x深度较大
//用“暴力”的思想:先让x,y跳到同一深度,然后一起往上跳
for(int i=;i>=;i--)//倒着for,x能多跳尽量多跳 ,才能优化时间
{
if(dep[f[x][i]]>=dep[y]) x=f[x][i];//先跳到同一层
if(x==y) return y;
}
for(int i=;i>=;i--)//此时x,y已跳到同一层
{
if(f[x][i]!=f[y][i])//如果 f[x][i]和f[y][i]不同才跳
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][];//跳完上述步骤后,两点离LCA仅一步之遥,让x(或y)再向上跳一步就是LCA。
}

倍增求LCA的板子题

题目描述:

  如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入输出格式:

输入格式:

  第一行包含三个正整数 N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

  接下来 N-1 行每行包含两个正整数 x、y,表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以构成树)。

  接下来 M 行每行包含两个正整数 a、b,表示询问 a 结点和 b 结点的最近公共祖先。

输出格式:

  输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<set>
#include<map>
#include<vector>
#include<fstream>
using namespace std;
#define maxn 501000
int n,m,s;
int dep[maxn<<];
int f[maxn<<][];
int head[maxn<<],cnt=;
struct hh
{
int nex,to;
}t[maxn<<];
inline void add(int nex,int to)
{
t[++cnt].nex=head[nex];
t[cnt].to=to;
head[nex]=cnt;
}
inline void Deal_first(int u,int fa)
{
dep[u]=dep[fa]+;
for(int i=;i<;i++)
f[u][i+]=f[f[u][i]][i];
for(int i=head[u];i;i=t[i].nex)
{
int v=t[i].to;
if(v==fa) continue;
f[v][]=u;
Deal_first(v,u);
}
return;
}
inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=;i>=;i--)
{
if(dep[f[x][i]]>=dep[y]) x=f[x][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][];
}
inline int read()
{
int kr=,xs=;
char ls;
ls=getchar();
while(!isdigit(ls))
{
if(!(ls^))
kr=-;
ls=getchar();
}
while(isdigit(ls))
{
xs=(xs<<)+(xs<<)+(ls^);
ls=getchar();
}
return xs*kr;
}
int main()
{
int x,y;
n=read();m=read();s=read();//n个节点,m个询问,以s为根节点
for(int i=;i<n;i++)
{
x=read();y=read();
add(x,y);
add(y,x);//添加无向边
}
Deal_first(s,);//以点s为根节点预处理 f 数组
for(int i=;i<=m;i++)
{
x=read();y=read();
printf("%d\n",LCA(x,y));
}
return ;
}

LCA 模板的更多相关文章

  1. LCA模板

    /*********--LCA模板--***************/ //设置好静态参数并构建好图的邻接表,然后调用lca_setquery()设置查询 //最后调用lca_start(),在lca ...

  2. 倍增求lca模板

    倍增求lca模板 https://www.luogu.org/problem/show?pid=3379 #include<cstdio> #include<iostream> ...

  3. HDU 2586——How far away ?——————【LCA模板题】

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  4. 算法复习——LCA模板(POJ1330)

    题目: Description A rooted tree is a well-known data structure in computer science and engineering. An ...

  5. hdu 2586 How far away?(LCA模板题+离线tarjan算法)

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  6. LCA模板(数剖实现)

    题目链接:https://www.luogu.org/problemnew/show/P3379 题意:LCA模板题. 思路:今天开始学树剖,先拿lca练练.树剖解lca,两次dfs复杂度均为O(n) ...

  7. POJ 1330 Nearest Common Ancestors(LCA模板)

    给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果 ...

  8. HDU2586 How far away ?(LCA模板题)

    题目链接:传送门 题意: 给定一棵树,求两个点之间的距离. 分析: LCA 的模板题目 ans = dis[u]+dis[v] - 2*dis[lca(u,v)]; 在线算法:详细解说 传送门 代码例 ...

  9. 最近公共祖先(LCA)模板

    以下转自:https://www.cnblogs.com/JVxie/p/4854719.html 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖 ...

  10. HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...

随机推荐

  1. [转载]tnsnames.ora监听配置文件详解

    监听配置文件             为了使得外部进程 如 CAMS后台程序 能够访问 Oracle 数据库 必须配             置 Oracle 网络服务器环境 配置 Oracle 网络 ...

  2. 如何通过命令行使用Wisdom RESTClient?

    Wisdom RESTClient V1.2版本开始支持命令行方式运行. 工具地址: https://github.com/Wisdom-Projects/rest-client 使用说明:java ...

  3. QT多线程简单例子

    在Qt中实现多线程,除了使用全局变量.还可以使用信号/槽机制. 以下例子使用信号/槽机制. 功能: 在主线程A界面上点击按钮,然后对应开起一个线程B.线程B往线程A发送一个字符串,线程A打印出来. 1 ...

  4. 原生态JDBC

    原生态JDBC JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API.JDBC是java访问数据库的标准规范,可以为不同的关系 ...

  5. linux下启动多个php,分别监听不同的端口。

    在工作中,我们可能会遇到,服务器集群的搭建. 这个时候,我们不可能,每一台服务器都是lnmp的环境,我们会把nmp分别放在不同的服务器上,不同的服务器负责不同的功能.比如我们下面要说的php 加入ng ...

  6. Yii笔记:打印sql、Form表单、时间插件、Mysql的 FIND_IN_SET函数使用、是否是post/ajax请求

    语句部分: yii1版本打印最后一条执行的SQL: $this->getDbConnection()->createCommand()->select()->from()-&g ...

  7. 【Selenium专题】元素定位之CssSelector

    CssSelector是我最喜欢的元素定位方法,Selenium官网的Document里极力推荐使用CSS locator,而不是XPath来定位元素,原因是CSS locator比XPath loc ...

  8. sqlloader parallel调用报ORA-26002: table has index defined upon it.解决方法

    ORA-26002: table has index defined upon it. This issue is caused when using the bulk load option in ...

  9. KPI 私有CA

    openssl总结及私有CA的搭建 搭建CA服务器 CA(证书颁发机构)服务器配置图解过程(1) 私有CA服务器的搭建 搭建CA服务器 使用OpenSSL搭建CA Linux加密和解密.openssl ...

  10. 【专家坐堂Q&A】在 petalinux-config 中选择外部来源时,可将符号链路添加内核来源目录树

    问题描述 作为 petalinux-config 菜单的一部分,现在可以将 Linux 内核指定为外部来源. 如果选择了该选项,可为内核来源目录树添加两个符号链路. 这会带来两个问题: 1. 符号链路 ...