算法之倍增和LCA:论点与点之间的攀亲戚
前言
我们在做树形题和图论题时常常遇到这样的问题:要求求出树上两点间的最近公共祖先(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:论点与点之间的攀亲戚的更多相关文章
- [算法]树上倍增求LCA
LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...
- [算法模板]倍增求LCA
倍增LCA \(fa[a][i]\)代表a的第\(2^{i}\)个祖先. 主体思路是枚举二进制位,让两个查询节点跳到同一高度然后再向上跳相同高度找LCA. int fa[N][21], dep[N]; ...
- 倍增求LCA算法详解
算法介绍: 看到lca问题(不知道lca是什么自(bang)行(ni)百度),不难想到暴力的方法: 先把两点处理到同一深度,再让两点一个一个祖先往上找,直到找到一个相同的祖先: 这么暴力的话,时间复杂 ...
- Codeforces 519E A and B and Lecture Rooms [倍增法LCA]
题意: 给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的.树上所有边的边权是1. 思路: 很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分 ...
- 【题解】POJ 3417 Network(倍增求LCA+DP+树上差分)
POJ3417:http://poj.org/problem?id=3417 思路 我们注意到由“主要边”构成一颗树 “附加边”则是非树边 把一条附加边(x,y)加入树中 会与树上x,y之间构成一个环 ...
- 【模板时间】◆模板·I◆ 倍增计算LCA
[模板·I]LCA(倍增版) 既然是一篇重点在于介绍.分析一个模板的Blog,作者将主要分析其原理,可能会比较无趣……(提供C++模板) 另外,给reader们介绍另外一篇非常不错的Blog(我就是从 ...
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- 树上倍增求LCA(最近公共祖先)
前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...
- 【倍增】洛谷P3379 倍增求LCA
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
随机推荐
- Codeforces 1684 E. MEX vs DIFF
题意 给你n个非负整数的数列a,你可以进行K次操作,每次操作可以将任意位置的数数更改成任意一个非负整数,求操作以后,DIFF(a)-MEX(a)的最小值:DIFF代表数组中数的种类.MEX代表数组中未 ...
- 快读《ASP.NET Core技术内幕与项目实战》EFCore2.5:集合查询原理揭秘(IQueryable和IEnumerable)
本节内容,涉及4.6(P116-P130).主要NuGet包:如前述章节 一.LINQ和EFCore的集合查询扩展方法的区别 1.LINQ和EFCore中的集合查询扩展方法,虽然命名和使用完全一样,都 ...
- 题解 AT2361 [AGC012A] AtCoder Group Contest
\(\sf{Solution}\) 显然要用到贪心的思想. 既然最终的结果只与每组强度第二大选手有关,那就考虑如何让他的值尽可能大. 其实,从小到大排个序就能解决,越靠后的值越大,使得每组强度第二大选 ...
- 【Java复健指南09】项目练习全解--房屋出租系统
一个基于文本界面的综合练习,主要用于串联和回忆知识点,比较简单 各个界面的设计样式 主菜单 =============房屋出租系统菜单============ 1 新 增 房 源 2 查 找 房 屋 ...
- MYSQL数据库的导出和导入
一.连接服务器查看数据库 使用连接工具(xshell6等)连接到数据库所在服务器,执行命令查询需要导出的数据库 1.输入数据库管理员账号密码进入控制台:mysql -uroot -p123456 ...
- Idea在windows和mac中的一些快捷指令
从 Windows 过度到 Mac 必备快捷键对照表 Mac 键盘符号说明 ⌘ == Command ⇧ == Shift ⇪ == Caps Lock ⌥ == Option ⌃ == Contro ...
- SpringBoot怎么自定义一个Starter ?
小伙伴们曾经可能都经历过整天写着CURD的业务,都没写过一些组件相关的东西,这篇文章记录一下SpringBoot如何自定义一个Starter. 原理和理论就不用多说了,可以在网上找到很多关于该方面的资 ...
- 除了 filter 还有什么置灰网站的方式?
大家都知道,当一些重大事件发生的时候,我们的网站,可能需要置灰,像是这样: 当然,通常而言,全站置灰是非常简单的事情,大部分前端同学都知道,仅仅需要使用一行 CSS,就能实现全站置灰的方式. 像是这样 ...
- VSCode解决Python中空格和制表符混用报错
Python对缩进的要求非常严格,缩进控制不正确可能会造成代码执行不正确甚至报错. 遇到报错"TabError: inconsistent use of tabs and spaces in ...
- Mattermost 笔记
目录 部署 配置 客户端 桌面程序 Android 使用 扩展 Jenkins Hubot 机器人 Jira GitHub Mattermost 是一个开源.可私有化部署的在线通讯平台,可以和Gith ...