前言

我们在做树形题和图论题时常常遇到这样的问题:要求求出树上两点间的最近公共祖先(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. Java多线程-线程关键字(二)

    Java中和线程相关的关键字就两:volatile和synchronized. volatile以前用得较少,以后会用得更少(后面解释).它是一种非常轻量级的同步机制,它的三大特性是: 1.保证可见性 ...

  2. 文盘Rust -- 把程序作为守护进程启动

    当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望shell关闭既停服.今天我们就来聊聊这个事儿. 最早大家部署应用的通常操作是 "nohu ...

  3. Python基础部分:4、 python语法之注释

    目录 一.python语法之注释 1.什么是注释 2.如何编写注释 二.PEP8规范 一.python语法之注释 1.什么是注释 注释用来向用户提示或解释某些代码的作用和功能,它可以出现在代码中的任何 ...

  4. python中的浅拷贝,深拷贝

    直接引用,间接引用 # 1.列表存储的是索引对应值的内存地址,值会单独的开辟一个内存空间 list = ["a","b"] 内存里面存储的就是list[0],l ...

  5. MybatisPlus Lambda表达式 聚合查询 分组查询 COUNT SUM AVG MIN MAX GroupBy

    一.序言 众所周知,MybatisPlus在处理单表DAO操作时非常的方便.在处理多表连接连接查询也有优雅的解决方案.今天分享MybatisPlus基于Lambda表达式优雅实现聚合分组查询. 由于视 ...

  6. 使用 Bytebase 管理 Rainbond 上的应用数据库

    在应用的发布过程中数据库的结构变更一直是最复杂也是风险最大的环节,而 Bytebase 可以对这一过程进行全生命周期的管理.在 Rainbond 中安装 Bytebase,轻松管理部署在 Rainbo ...

  7. updog:一款局域网传输文件的软件

    前言 不知道你是否有过这样 的需求,在局域网传输文件,苦于没有好的软件支持,或者只能单向传输,updog可以完全解决这种问题 安装 pip3 install updog updog是python中的一 ...

  8. 使用Supervisor监控mysql

    Supervisor安装教程参考:https://www.cnblogs.com/brad93/p/16639953.html mysql安装教程参考:https://www.cnblogs.com/ ...

  9. 《MySQL必知必会》知识汇总一

    一.使用MYSQL 展示所有数据库 show databases; 选择数据库 use crashcourse; 展示该数据库中所有的表 show tables; 还可以展示表列的shema约束 sh ...

  10. Java语言进阶 day02【Collection、泛型】

    主要内容 Collection集合 迭代器 增强for 泛型 教学目标