LCA—倍增法求解
LCA(Least Common Ancestors)
即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。
常见解法一般有三种
这里讲解一种在线算法—倍增
首先我们定义fa[u][j]表示结点u的第2^j祖先
那么要怎么求出全部的fa数组呢
不难发现fa[u][0]就是u的父亲结点
这些父亲结点我们可以直接初始化
对于其他结点则有
fa[u][j]=fa[ fa[u][j-1] ] [j-1]
什么意思呢
u的第2^(j-1)祖先的第2^(j-1)祖先 就是u的第2^j祖先(有点像快速幂那样分解)
预处理各节点深度+初始fa[u][0]
void dfs(int u,int pa)
{
dep[u]=dep[pa]+1;
fa[u][0]=pa;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v!=pa) dfs(v,u);
}
}
预处理fa数组
for(int i=1;(1<<i)<=n;i++)
for(int u=1;u<=n;u++)
fa[u][i]=fa[ fa[u][i-1] ][i-1];
预处理之后怎么求解LCA(u,v)呢
我么先假定dep[u]>dep[v]
则两点深度差 d=dep[u]-dep[v]
现在我们要做的是把u升到与v同样的深度
怎么做呢? 先贴代码
for(int i=0;(1<<i)<=d;i++)
if( (1<<i) & d ) x=fa[x][i];
对于任意一个d
我们都能将其分解为d=2^p1+2^p2+……2^pi
这可以用二进制实现
例如d=5 ,5的二进制是101
我们将其分解为100+1
而100的十进制是4,1的十进制是1!!!
4=2^2 1=2^0
5=2^2 +2^0
是不是好神奇!!!!(欢呼)
应用到这里
就是查询d的二进制哪些位是1
查询到第i位为1
我们就将u向上升2^i个深度
这样一定能升到与v同深度
(注意二进制最右边一位是第0号位)
if( (1<<i) & d )
这一段用来检查d的二进制下第i位是否为1
抬升到相同高度后就可以开始查询LCA了
同样先上代码
for(int i=(int)log(n);i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
大体思路就是
从u和v最远的祖先开始
如果u的第2^i祖先等于 v的第2^i祖先,就不移动
否则u和v同时上移2^i个深度
最后u的父亲一定是u和v的LCA
(其实蒟蒻不是特别理解其中的玄学道理,但手动模拟了几组发现真的是这样)
妙啊~妙啊~
最后贴上一份完整代码
模板题传送门啦~啦~啦~
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
void print(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,s;
int tot;
struct node{int v,nxt;}E[1000010];
int head[1000010];
int fa[5000010][25];
int dep[1000010];
void add(int u,int v)
{
E[++tot].nxt=head[u];
E[tot].v=v;
head[u]=tot;
}
void dfs(int u,int pa)
{
dep[u]=dep[pa]+1;
fa[u][0]=pa;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v!=pa) dfs(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]-dep[y]<0) swap(x,y);//将u设为深度较深的结点
int d=dep[x]-dep[y];
for(int i=0;(1<<i)<=d;i++)
if( (1<<i) & d ) x=fa[x][i];//抬升
if(x==y) return x;//抬升后x==y,则其LCA就是y(或此时的x)
else
{
for(int i=(int)log(n);i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
}
int main()
{
n=read();m=read();s=read();
for(int i=1;i<=n-1;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs(s,-1);
//无根树转有根树,在这里调用是pa要设为-1
//预处理fa数组
for(int i=1;(1<<i)<=n;i++)
for(int u=1;u<=n;u++)
fa[u][i]=fa[ fa[u][i-1] ][i-1];
while(m--)
{
int x=read(),y=read();
int ans=lca(x,y);
print(ans); printf("\n");
}
return 0;
}
LCA—倍增法求解的更多相关文章
- LCA(最近公共祖先)——LCA倍增法
一.前人种树 博客:最近公共祖先 LCA 倍增法 博客:浅谈倍增法求LCA 二.沙场练兵 题目:POJ 1330 Nearest Common Ancestors 代码: const int MAXN ...
- POJ - 1330 Nearest Common Ancestors(dfs+ST在线算法|LCA倍增法)
1.输入树中的节点数N,输入树中的N-1条边.最后输入2个点,输出它们的最近公共祖先. 2.裸的最近公共祖先. 3. dfs+ST在线算法: /* LCA(POJ 1330) 在线算法 DFS+ST ...
- 浅谈倍增法求解LCA
Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...
- hdu2586 lca倍增法
倍增法加了边的权值,bfs的时候顺便把每个点深度求出来即可 #include<iostream> #include<cstring> #include<cstdio> ...
- poj1470 LCA倍增法
倍增法模板题 #include<iostream> #include<cstring> #include<cstdio> #include<queue> ...
- 最近公共祖先 LCA 倍增法
[简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...
- luogu3379 【模板】最近公共祖先(LCA) 倍增法
题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...
- LCA - 倍增法去求第几个节点
You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, ...
- POJ 1330(LCA/倍增法模板)
链接:http://poj.org/problem?id=1330 题意:q次询问求两个点u,v的LCA 思路:LCA模板题,首先找一下树的根,然后dfs预处理求LCA(u,v) AC代码: #inc ...
随机推荐
- 在iOS App 中添加启动画面
你可以认为你需要为启动画面编写代码,然而Apple 让你可以非常简单地在Xcode中完成.不需要编写代码,你仅需要在Xcode中进行一些配置. 1.什么是启动画面(Splash Screen)? 启动 ...
- sqlite3-入门教程
*** 非常好 http://wenku.baidu.com/link?url=xDoS4prJaGRac0QS0nxdNr6056N-qJIfZ7OhiTharj1ntuIsNy9sWXWvBXLh ...
- Python+Selenium安装及环境配置
一.Python安装 Window系统下,python的安装很简单.访问python.org/download,下载最新版本,安装过程与其他windows软件类似.记得下载后设置path环境变量,然后 ...
- Java进阶篇(五)——Java的I/O技术
程序中,为了永久的保存创建的数据,需要将其保存在磁盘文件中,以便在其它程序中使用它们.Java的I/O技术可以将数据保存到文本文件.二进制文件甚至是ZIP压缩文件中,以达到永久性保存数据的要求. 本篇 ...
- 手把手教学系列:从零开始配置VPS服务器
1.什么是VPS? 百度百科:VPS(Virtual Private Server 虚拟专用服务器)技术,将一台服务器分割成多个虚拟专享服务器的优质服务. 通俗地讲,可以认为就是一台放在机房机架上的服 ...
- 有史以来功能最全,使用最简单的excel导入/导出工具
Github地址:https://github.com/xuanbg/Utility. 还有其他一些福利,请各位园友自取. 构造方法 1.用于导出Excel文件 NpoiHelper(ExcelVer ...
- 用Python实现几种排序算法
#coding=utf-8 # 1 快速排序算法 def qksort(list): if len(list)<=1: return list else: pivot = list[0] les ...
- 安卓studio导入jra包和so包,百度地图so包加载
导入so包 这个我只接受测试可用的一种方法 第一步:把so包放在libs目录下,可以是文件夹也可以是单独的一个个so文件 然后在src同级的目录下找到build.gradle文件下如下信息 sourc ...
- 图片文档倾斜矫正算法 附完整c代码
2年前在学习图像算法的时候看到一个文档倾斜矫正的算法. 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理. 相关的关键词: 抗倾斜 ...
- Java基础知识(一)
类与对象 1.对象:客观存在的一切事物称之为对象 类:具有相同属性和方法的对象的集合 2.类:属性,方法 3.修饰符: public protected 默认(不写) private 任何地方 ...