LCA 模板
关于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 模板的更多相关文章
- LCA模板
/*********--LCA模板--***************/ //设置好静态参数并构建好图的邻接表,然后调用lca_setquery()设置查询 //最后调用lca_start(),在lca ...
- 倍增求lca模板
倍增求lca模板 https://www.luogu.org/problem/show?pid=3379 #include<cstdio> #include<iostream> ...
- HDU 2586——How far away ?——————【LCA模板题】
How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- 算法复习——LCA模板(POJ1330)
题目: Description A rooted tree is a well-known data structure in computer science and engineering. An ...
- 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 ...
- LCA模板(数剖实现)
题目链接:https://www.luogu.org/problemnew/show/P3379 题意:LCA模板题. 思路:今天开始学树剖,先拿lca练练.树剖解lca,两次dfs复杂度均为O(n) ...
- POJ 1330 Nearest Common Ancestors(LCA模板)
给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果 ...
- HDU2586 How far away ?(LCA模板题)
题目链接:传送门 题意: 给定一棵树,求两个点之间的距离. 分析: LCA 的模板题目 ans = dis[u]+dis[v] - 2*dis[lca(u,v)]; 在线算法:详细解说 传送门 代码例 ...
- 最近公共祖先(LCA)模板
以下转自:https://www.cnblogs.com/JVxie/p/4854719.html 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖 ...
- HDU 2586 How far away ?(LCA模板 近期公共祖先啊)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...
随机推荐
- [转载]oracle的常用函数 instr() 和substr()函数
在Oracle中 可以使用instr函数对某个字符串进行判断,判断其是否含有指定的字符. 在一个字符串中查找指定的字符,返回被查找到的指定的字符的位置. 语法: instr(sourceString, ...
- TensorFlow for distributed
TensorFlow for distributed 本目录包括了运行时分布式TensorFlow的实现,其底层使用了gRPC 作为进程内通信的支持库. Quick start 首先,需要构建一个Te ...
- Django框架----路由系统(详细)
Django的路由系统 Django 1.11版本 URLConf官方文档 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表. ...
- 2018.1.7java转型
从昨天的组合和继承开始,我在分析二者的区别,到后面的向上转型,再到hashcode和equals还有==的区别,感觉基础还很不好,也许,目前应该从面向对象开始复习,把暂时笔试宝典放一下. 回忆一下今天 ...
- Golang并发编程有缓冲通道和无缓冲通道(channel)
无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...
- css链接link
链接可以使用任何css属性,包括字体.颜色.背景等等. 链接有四个状态,可在四个状态时设置不同的属性 a:link - 正常,未访问过的链接 a:visited - 用户已访问过的链接 a:hover ...
- Spring Boot 2 (二):Spring Boot 2 动态 Banner
Spring Boot 2 (二):Spring Boot 2 动态 Banner Spring Boot 2.0 提供了很多新特性,其中就有一个小彩蛋:动态 Banner. 一.配置依赖 使用 Sp ...
- linux 文件 md5校验
为解决官方发布的软件包被别人更改或者软件在传输过程中出现传输错误等问题,软件官方在提供软件包的同时,还提供一个保存MD5校验码的文件. Linux/unix中可以使用命令 # md5sum 文件名 方 ...
- HTTP小幺鸡接口管理工具安装与配置说明
http://www.xiaoyaoji.cn/doc/TxybXPTdx 小幺鸡接口管理工具安装说明 使用可以参考:https://blog.csdn.net/qincidong/article/d ...
- API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd(转)
前几天拜读了 OpsGenie 公司(一家致力于 Dev & Ops 的公司)的资深工程师 Turgay Çelik 博士写的一篇文章(链接在文末),文中介绍了他们最初也是采用 Nginx 作 ...