之前在澡堂学过这么个东西,听课时理解非常透彻,然后做题时是这种状态:



因为并没有切板子题,最近切掉以后看同桌,他默默地说了一句话:

我是什么时候A的来着...



我当时就心态爆炸...

现在来进行简单整理

我发现想黈之前的博客非常难,因为之前写的博客都是什么东西啊

其实我本身来讲也能理解(疯狂为下次培训不好好整理找理由)

以为澡堂给的时间其实并不多,看上去有一中午加一晚上,

但是每天学的东西都非常之多,要是把每个板子题都切下,博客中的最详细内容就只能gu掉了...

心塞...

(突然发现自己水了这么多行)

最近公共祖先在求两点最短距离时非常常用,

其定义就是两点\(u\),\(v\)的公共祖先,并且满足这个公共祖先深度尽可能大

就是这么一个东西:

求ta的思路当然就是枚举,然而首先拿到的两个点不一定在一层,并不能直接进行暴力枚举,

那么不能将这两点调到一层以后再一个一个暴力枚举咩?

有优化算法废话还辣么多干嘛

这里介绍的方法是倍增

就是考虑往上跳\(2^n\)个祖宗,加上一定的判断,显然能够精确地跳到LCA,这样显然非常快

倍增的大体算法流程就是先将深度比较大的那个点跳到与另一个点同一深度的位置,如果此时重合,就返回其中一个点

然后一起进行倍增,直到两点的父亲为同一点,则返回其父亲



其实现思想如下(按程序步骤):

1.读入并建边建树

单纯读入并不想讲...

需要注意的是,在建边时需要建无向边,因为并不知道谁是爹

所以建无向边便于在以后进行建树操作,顺便处理出点的深度以及所有跳的爹

就是处理出这个节点的上面的第\(2^n\)个爹,(这里的\(n\)没有啥正经实际意义)

建树操作就是跑一个\(DFS\),p.s.我在某次考试用的时\(BFS\),事实证明都可以,

就建个树嘛...还能卡死哪种搜索?

\(DFS\)建树的主要思路就是拿到当前点,先处理深度,就是\(dep[爹]+1\),

这个"爹"好出戏...

然后将其"跳爹"处理出来,当然是基于之前处理过的"跳爹"

然后就是深搜主体,便利出边,将非爹节点认儿子,并进行下一步搜索,

因为建的无向边,所以儿子向着爹也有一条边,但由于爹的唯一性,这个指向爹的边有且仅有一条

同时在递归搜索时,把当前点的编号和子节点的编号一同下传,也就是说在每一次开始搜索时,都有当前节点和当前节点之爹这两个信息传下来

然后就是访问查询,(这样的题肯定不会单组询问)

写一个函数专门查询,

查询函数步骤(结合代码看下,\(f\)数组指的是跳爹):

inline int lca(int l,int r){
if(dep[l]<dep[r]) swap(l,r);
for(int i=20;i>=0;i--){
if(dep[f[l][i]]>=dep[r])l=f[l][i];
if(l==r) return l;
}
for(int i=20;i>=0;i--){
if(f[l][i]!=f[r][i]){
l=f[l][i];
r=f[r][i];
}
}return f[l][0];
}

1.使方便处理的节点深度大,就是函数内第一行的意义,这样好处理

2.使两节点跳到同一层,当然要让\(i\)从大到小枚举,这样满足倍增的思想

好像这是正文里第一次出现倍增这个词...

就是先跳大步,跳不了跳小步,通过预处理各种条件(比如深度和\(2^n\)个祖宗)来提供判断所需依据

当然如果当两节点在同一层时也在同一点,那么他们所在位置已经是LCA了,这种情况的出现当且仅当访问数据中出现存在父子(或祖孙啥的)关系

那么下面就是倍增主环节

同样\(i\)要从大到小枚举,道理相同

然而这里的判断条件是"祖宗不同就跳"

毕竟不能跳过嘛,比如在这棵树中:

显然\(f[8][1]==f[9][1]==2\),然而ta们的LCA并不是2,

所以就是跳过了

所以判断的条件就是爹不统一,

按照这个法则跳可以始终保证目前节点在LCA下面

这样跳到的最终结果就是其LCA的儿子

此时返回\(f[l][0]\)就行辣\(≧▽≦)/

完整代码奉上:

#include<bits/stdc++.h>
using namespace std;
int n,m,s;
struct Ed{
int to,nxt;
}ed[1000005];
int head[1000005];
int dep[1000005];
int f[1000005][21];
int ednum;
inline void add(const int &from,const int &to){
ed[++ednum].nxt=head[from];
ed[ednum].to=to;
head[from]=ednum;
}
inline void search(const int &u,const int &fa){
dep[u]=dep[fa]+1;
for(int i=1;(1<<i)<=dep[u];i++)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to;
if(v==fa) continue;
f[v][0]=u;
search(v,u);
}
}
inline int lca(int l,int r){
if(dep[l]<dep[r]) swap(l,r);
for(int i=20;i>=0;i--){
if(dep[f[l][i]]>=dep[r])l=f[l][i];
if(l==r) return l;
}
for(int i=20;i>=0;i--){
if(f[l][i]!=f[r][i]){
l=f[l][i];
r=f[r][i];
}
}return f[l][0];
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
search(s,0);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}return 0;
}

突然想起还有单调队列板子要整,

可是要放假了啊

窗外...白鸽飞过...

最近公共祖先(LCA)基础模板(倍增法)的更多相关文章

  1. 最近公共祖先 LCA 倍增法

    [简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. 最近公共祖先算法LCA笔记(树上倍增法)

    Update: 2019.7.15更新 万分感谢[宁信]大佬,认认真真地审核了本文章,指出了超过五处错误捂脸,太尴尬了. 万分感谢[宁信]大佬,认认真真地审核了本文章,指出了超过五处错误捂脸,太尴尬了 ...

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

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

随机推荐

  1. linux下安装make工具

    安装make工具 make工具依赖gcc ,automake,autoconf,libtool,make 这些安装包 可以一起安装 center os系统 运行如下命令yum install gcc ...

  2. Spring Security安全框架

    今天来简单介绍一下Spring Security安全框架 简介 Spring Security 提供了基于javaEE的企业应有个你软件全面的安全服务.这里特别强调支持使用SPring框架构件的项目, ...

  3. 连接查询 变量、if else、while

    连接查询 变量.if else.while   一.连接查询:通过连接运算符可以实现多个表查询. 连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志. 常用的两个链接运算符: ...

  4. 表单生成器(Form Builder)之mongodb表单数据查询——关联查询

    这一篇接着记录一下查询相关的操作.想象一下,如果想要在一张表格中展示某些车辆的耗损和营收情况,我们该怎么处理.车辆.耗损.营收各自存储在一张表中,耗损和营收中冗余了车辆信息……我们便想到了关联查询.m ...

  5. Docker和ASP.NET Core

    Docker和ASP.NET Core Docker 正在逐渐成为容器行业的事实标准,受到 Windows 和 Linux 生态系统领域最重要供应商的支持. (Microsoft 是支持 Docker ...

  6. 对numpy.meshgrid()理解

    一句话解释numpy.meshgrid()——生成网格点坐标矩阵.关键词:网格点,坐标矩阵 网格点是什么?坐标矩阵又是什么鬼?看个图就明白了: 图中,每个交叉点都是网格点,描述这些网格点的坐标的矩阵, ...

  7. 弹指间,网页灰飞烟灭——Google灭霸彩蛋实现

    不知道大家有没有看这段时间最火的一部电影<复仇者联盟4:终局之战>,作为漫威迷的我还没看,为什么呢?因为太贵了,刚上映的那周,一张IMAX厅的票价已经达到了299的天价,作为搬砖民工是舍不 ...

  8. Dynamics 365 Online-Delete Audit History Data

    Dynamics 365 CE自带的Audit功能,虽然不会给我们的业务流程带来显著变化,但是这个功能对于我们追溯数据变化的历史,诊断定制触发的执行,以及数据还原等,都是不可或缺的关键先生.尤其是涉及 ...

  9. Hadoop完全分布式搭建流程

    centos7 搭建完全分布式 Hadoop 环境  SSR 前言 本次教程是以先创建 四台虚拟机 为基础,再配置好一台虚拟机的情况下,直接复制文件到另外的虚拟机中(这样做大大简化了安装流程) 且本次 ...

  10. MD5是个好东西 / MD5 is a nice guy

    md5是一种摘要生成算法,通过对消息生成唯一摘要,可校验消息是否被篡改. 众所周知,md5广泛用在http接口通讯的安全控制上,通过在签名原始串后加上商户通信秘钥,进行MD5运算,形成的摘要字符串即为 ...