前言

我们在做树形题和图论题时常常遇到这样的问题:要求求出树上两点间的最近公共祖先(LCA),这时我们该怎么办?

思路一:暴力爬爬爬……

很容易想到让两个点都往上爬,啥时候相遇了就是他们的最近公共祖先。

但是这实在是太慢了啊!怎么办呢?

思路二:倍增思想

倍增思想来源于数学。

首先,可以证明任意整数可以写成二进制形式(废话)。

然后,可以证明向上爬的层数不是正整数就是0(又是废话)。

最后,如果每次爬的层数是其二进制中的每一位,即成二的几次方的往上爬,可以证明一定能爬到他们的LCA!(恍然大悟)

如何实现

你比方说,我要向上爬个\(n\)层。这个\(n\)比如说是\(114514\)

那他的二进制就是:

\(11 011 111 101 010 010\)

我们怎么爬?

既然是成二的几次方的爬,那我们从最左边开始吧!

我们可以发现,114514的二进制最左边一位是1,代表了\(2^{17}\),二的17次方。

维护一个计数器,记录我们爬了多少。

可以发现,爬了\(2^{17}\)层后,没有超过114514,那就看下一位:\(2^{16}\)

如果这一位爬了,也是不越界的,那就继续爬

但是\(2^{15}\)这一位,如果爬了就越界了!所以此时我们要舍弃这一位,继续去看下一位能不能爬。

这就大大减少了我们向上爬的时间,这就是倍增思想。

LCA代码实现

请看代码,这同时是这道题的AC代码:

#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0'){
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^'0');
c=getchar();
}
return x;
}//快读不用管
int n,m,s,x,y;
vector<int>tree[500001];//树
int dep[500001];//深度
int f[500001][21];//f[i][j]表示i节点的2的j次方祖先是谁
void dfs(int now,int d,int father){
dep[now]=d;//记录深度
f[now][0]=father;//爸爸是爸爸
for(int i=1;(1<<i)<=dep[now];i++){
f[now][i]=f[f[now][i-1]][i-1];//预处理祖先,数学原理:2^i=2^(i-1)+2^(i-1)
}
for(int i=0;i<tree[now].size();i++){//遍历相连的边
if(tree[now][i]!=father)dfs(tree[now][i],d+1,now);//如果不是爸爸,就dfs
}
}
int lca(int u,int v){
int temp;
if(dep[u]<dep[v])swap(u,v);
temp=dep[u]-dep[v];//记录深度差
for(int i=0;(1<<i)<=temp;i++){
if((1<<i)&temp){
u=f[u][i];
}
}//这里是先把那个低一点的爬到和高一点的齐平
if(u==v)return u;
for(int i=(int)(log(n)/log(2));i>=0;i--){//一位位爬,这里这个log的用法用到了对数函数换底公式,不用理解太透
if(f[u][i]!=f[v][i]){//如果祖先不同,因为超限就是未定义,未定义就是0,会祖先相同所以可以用来防止超限
u=f[u][i];//爬
v=f[v][i];
}
}
return f[u][0];//最后两个的爸爸就是LCA
}
int main(){
n=read();//节点数
m=read();//询问个数
s=read();//根节点
for(int i=1;i<n;i++){
x=read();
y=read();
tree[x].push_back(y);
tree[y].push_back(x);
//邻接表建树也不用说
}
dfs(s,1,0);//注意!首先一边DFS进行预处理
for(int i=1;i<=m;i++){
x=read();
y=read();
printf("%d\n",lca(x,y));//求LCA
}
return 0;
}

算法之倍增和LCA:论点与点之间的攀亲戚的更多相关文章

  1. [算法]树上倍增求LCA

    LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...

  2. [算法模板]倍增求LCA

    倍增LCA \(fa[a][i]\)代表a的第\(2^{i}\)个祖先. 主体思路是枚举二进制位,让两个查询节点跳到同一高度然后再向上跳相同高度找LCA. int fa[N][21], dep[N]; ...

  3. 倍增求LCA算法详解

    算法介绍: 看到lca问题(不知道lca是什么自(bang)行(ni)百度),不难想到暴力的方法: 先把两点处理到同一深度,再让两点一个一个祖先往上找,直到找到一个相同的祖先: 这么暴力的话,时间复杂 ...

  4. Codeforces 519E A and B and Lecture Rooms [倍增法LCA]

    题意: 给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的.树上所有边的边权是1. 思路: 很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分 ...

  5. 【题解】POJ 3417 Network(倍增求LCA+DP+树上差分)

    POJ3417:http://poj.org/problem?id=3417 思路 我们注意到由“主要边”构成一颗树 “附加边”则是非树边 把一条附加边(x,y)加入树中 会与树上x,y之间构成一个环 ...

  6. 【模板时间】◆模板·I◆ 倍增计算LCA

    [模板·I]LCA(倍增版) 既然是一篇重点在于介绍.分析一个模板的Blog,作者将主要分析其原理,可能会比较无趣……(提供C++模板) 另外,给reader们介绍另外一篇非常不错的Blog(我就是从 ...

  7. 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))

    倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...

  8. [学习笔记] 树上倍增求LCA

    倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...

  9. 树上倍增求LCA(最近公共祖先)

    前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...

  10. 【倍增】洛谷P3379 倍增求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

随机推荐

  1. Vulnhub 靶机 pwnlab_init 渗透——详细教程

    1. 下载 pwnlab_ini 靶机的 .ova 文件并导入 VMware: pwnlab下载地址:PwnLab: init ~ VulnHub 导入VMware时遇到VMware上虚机太多,无法确 ...

  2. golang中的几种并发模式

    0.1.索引 https://blog.waterflow.link/articles/1663551951058 1.for- select模式 这种模式通常用在从多个通道读取数据 package ...

  3. iptables和firewalld基础

    1.四表五链概念: filter表 过滤数据包 Nat表 用于网络地址转换(IP.端口) Mangle表 修改数据包的服务类型.TTL.并且可以配置路由实现QOS Raw表 决定数据包是否被状态跟踪机 ...

  4. Mysql+Mycat+NFS+Rsync+LVS+DNS+IPtables综合实验

    1.环境准备 服务器 IP地址 作用 系统版本 Mysql-master eth0:10.0.0.58 主数据库 Rocky8.6 Mysql-slave1 eth0:10.0.0.68 备数据库 R ...

  5. go GMP

    动态栈 操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine 的初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建 ...

  6. 嵌入式-C语言基础:指针偏移打印数组

    在C语言中,数组名代表数组中首元素的地址,所以,下面两句获取数组的首地址是等价的: #include<stdio.h> int main() { int a[5]={1,2,3,4,5}; ...

  7. 优雅的重启uwsgi 告别uwsgi reload过程中造成的无法请求、请求延迟等问题

    [uwsgi]#使用优雅重启 lazy-apps = true #监听monitor文件 当monitor文件发生改变是重启uwsgi touch-chain-reload = /home/monit ...

  8. 【OpenStack云平台】SecureCRT 连接 CentOS虚拟机

    1.安装SecureCRT SecureCRT是一款支持SSH等协议的终端仿真软件,可以在windows下登录Linux服务器,这样大大方便了开发工作.安装SecureCRT可以通过网上的各种教程安装 ...

  9. 使用repo上传代码

    前言~ repo是一款安卓用于管理源码的工具,由python实现,基于git工具 本文介绍了repo的常用使用方式. 一,下载代码 1. repo init 初始化命令 此命令常用选项就那几个,此处取 ...

  10. B-神经网络模型复杂度分析

    前言 一,模型计算量分析 卷积层 FLOPs 计算 全连接层的 FLOPs 计算 二,模型参数量分析 卷积层参数量 BN 层参数量 全连接层参数量 三,模型内存访问代价计算 卷积层 MAC 计算 四, ...