算法之倍增和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行每 ...
随机推荐
- Python学习三天计划-3
面向对象 一.类的定义 1.类定义 class是关键字,表示要定义类了 类的属性,即定义在类中的变量(成员变量) 类的行为,即定义在类中的函数(成员方法) 2.对象 创建类对象的语法: class S ...
- pip cmd下载速度慢解决方案
cmd下载速度慢不是电脑问题,而是下载的网站有网速限制,如pip,虽然没被墙,但由于是外网,网速极差,经常是几KB一秒,所以我们可以采用镜像服务器,即在命令后加上 -i https://pypi.tu ...
- 一、Vue.js介绍
一.介绍 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手 ...
- 四、redis数据类型
四.redis数据类型 redis可以理解成一个全局的大字典,key就是数据的唯一标识符.根据key对应的值不同,可以划分成5个基本数据类型. 1. string类型: 字符串类型,是 Redis 中 ...
- Springboot数据库的配置问题
mysql时区问题 先前的代码如下 spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: ...
- C语言白盒测试讲义
好久没有做过C语言的白盒测试了,估计以后也没这个机会.把自己之前参加过的培训素材做个分享. 素材下载链接:https://pan.baidu.com/s/1LPD9Az04zEj8RuCICaKYxQ ...
- Guess Next Session
打开又是一个输入框的界面,点一下下面的看源码 很简短的一个源码 大概意思是如果password等于session[password]就输出flag 直接搜了下session函数的漏洞,发现sessio ...
- JUC面试点汇总
JUC面试点汇总 我们会在这里介绍我所涉及到的JUC相关的面试点内容,本篇内容持续更新 我们会介绍下述JUC的相关面试点: 线程状态 线程池 Wait和Sleep Synchronized和Lock ...
- Promise基础知识
Promise 1.Promise的前置小知识 进程(厂房) 程序的运行环境 线程(工人) 线程是实际进行运算的东西 同步 通常情况代码都是自上向下一行一行执行的 前边的代码不执行后边的代码也不会执行 ...
- GitHub上的一个笔记相关小项目
就是一个笔记屑小项目, C++编写,有想一起开发的私信 AlgorithWeaver/V-note (github.com) 项目名V-note QVQ