一般来求LCA有3种方法

1.倍增

2.RMQ+欧拉序

3.tarjan(离线)

本文将倍增求lca

这个算法是很常见很常见的

也是较好理解的

(我也不明白假期学长讲的时候我为什么死活都不明白

 自闭qwq

 对不起学长qwq

 明明学长讲的是最好的qwq

 想学长了qwq)

---------------------------------------------------------------------------

一、基础概念

LCA定义:

  LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。

如图,结点4,6的公共祖先有1、2,但最近的公共祖先是2,即Lca(4,6) = 2

显然遇到这个问题

首先会想到暴力

再一算

时间复杂度"仅仅"是O(n)而已啦

可是如果有q次询问呢

比如说洛谷板子题

n和q(这里的q即板子题中的m都是5 * 105的级别的

那么n*q就是一个不小的数了

那么暴力一定是过不了的了

那么就只能优化优化了

--------------------------------------------------------------------

倍增法:

注意到u,v走到最近公共祖先w之前,u,v所在结点不相同。

而到达最近公共祖先w后,再往上走仍是u,v的公共祖先,即u,v走到同一个结点,这具有二分性质。

于是可以预处理出一个2k的表,

fa[k][u]表示u往上走2k步走到的结点,令根结点深度为0,则2k>depth[u]时,令fa[k][u]=-1(不合法情况的处理)

不妨假设depth[u] < depth[v]

①将v往上走d = depth[v] - depth[u]步,此时u,v所在结点深度相同,该过程可用二进制优化。

由于d是确定值,将d看成2的次方的和值,d = 2k1 + 2k2 + ... + 2km,利用fa数组,如v = fa[k1][v],v = fa[k2][v]加速。

②若此时u = v,说明Lca(u,v)已找到

③利用fa数组加速u,v一起往上走到最近公共祖先w的过程。

令d = depth[u] - depth[w],虽然d是个未知值,但依然可以看成2的次方的和。

从高位到低位枚举d的二进制位,设最低位为第0位,若枚举到第k位,有fa[k][u] != fa[k][v],则令u = fa[k][u],v = fa[k][v]。

最后

最近公共祖先w = fa[0][u] = fa[0][v],即u和v的父亲

 

如何预处理?
    k=0时,fa[k][u]为u在有根树中的父亲,令根结点fa[k][root]=-1。

 k>0时,fa[k][u]=fa[k-1][fa[k-1][u]]。树的高度最多为n,k是logn级别。

时间复杂度?
    预处理O(nlogn)
    单次查询O(logn)

来个板子题也很好理解呀(传送门

上面提到过的

罗姑上也有板子题

但是我最近是由hdu的oj写的

正好还有那个的代码就直接拿过来了

#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 40000
using namespace std; struct EDGE
{
int nxt,to,v;
}edge[maxn*+];
//链式前向星(可以用结构体,也可以用独立的数组)
//注意这里结构体的大小,下面会说的 int T,n,root,cnt,m;
//T次模拟,cnt为边的个数(边的编号),n个点的树,m次询问,root是树的根(有的lca中没有给出根节点的标号,需要自己找,这里就没给)
int head[maxn+],dep[maxn+],dis[maxn+];
//head也是属于链前中的内容,写在这里因为[]中为点的标号,所以它的大小就是节点数的大小
//dep存每个节点的深度,dis存每个节点到根节点的深度
int f[maxn+][];//前面的[i]为i节点 ,后面的[j]为从i节点往上跳2^j个节点
bool vis[maxn+];//用来找根节点特意开的一个数组 void add(int x,int y,int z)//加边(用链式前向星存边)
{
edge[++cnt].to=y;
edge[cnt].v=z;
edge[cnt].nxt=head[x];
head[x]=cnt;
} void dfs(int u,int fa)
{
dep[u]=dep[fa]+;//深度加一
for(int i=; i<=; i++)//预处理,处理出u节点蹦2^i个节点所到达的节点编号
{
f[u][i+]=f[f[u][i]][i];
}
for(int i=head[u]; i; i=edge[i].nxt)//遍历他的出边(dfs)
{
if(edge[i].to==fa)
{
continue;
}//(因为正反都加边了)如果这个边指向的点是u节点的父节点,不dfs这个
dis[edge[i].to]=dis[u]+edge[i].v;//距离
f[edge[i].to][]=u;//父节点
dfs(edge[i].to,u);
}
} int LCA(int x,int y)
{
if(dep[x]<dep[y])
{
swap(x,y);
}//使x是最深的那个点
for(int i=; i>=; i--)
{
if(dep[f[x][i]]>=dep[y])//如果蹦的话x的深度仍比y的深度深,那就蹦,否则不蹦
{
x=f[x][i];
}
if(x==y)//如果x蹦到了y,那么此时的x(也就是y)就是lca
{
return x;
}
}
//此时x和y的深度相同,但不是同一个节点
for(int i=; i>=; i--)
{
if(f[x][i]!=f[y][i])//如果x和y蹦完了到了相同的节点,那么蹦到的节点一定大于或等于lca
{//所以只有蹦到了不同的节点,才可以蹦(这样才能保证没蹦超出lca)
x=f[x][i];
y=f[y][i];
}
}
return f[x][];//离真正的lca只差一步(此时不管蹦多少都超,所以前面的循环只能使x是离lca只差一层的节点)
} int main()
{
scanf("%d",&T);
while(T--)//T次模拟(每次询问都是用新的树,因此要把之前的数组全部清空)
{
memset(vis,,sizeof(vis));
memset(edge,,sizeof(edge));
memset(f,,sizeof(f));
memset(dep,,sizeof(dep));
memset(head,,sizeof(head));
memset(dis,,sizeof(dis));
cnt=;
scanf("%d%d",&n,&m);
for(int i=; i<=n-; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
vis[y]=;
add(x,y,z);
add(y,x,z);//因为不知道x和y谁是爸爸,所以就正反都加边 ,所以结构体的大小要开两倍
}
for(int i=; i<=n; i++)
{
if(vis[i]==)
{
root=i;
break;
}
}//没有被标记过的是根节点
dfs(root,);
for(int i=; i<=m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",dis[a]+dis[b]-*dis[LCA(a,b)]);
}
}
return ;
}

LCA--倍增法的更多相关文章

  1. LCA(最近公共祖先)——LCA倍增法

    一.前人种树 博客:最近公共祖先 LCA 倍增法 博客:浅谈倍增法求LCA 二.沙场练兵 题目:POJ 1330 Nearest Common Ancestors 代码: const int MAXN ...

  2. POJ - 1330 Nearest Common Ancestors(dfs+ST在线算法|LCA倍增法)

    1.输入树中的节点数N,输入树中的N-1条边.最后输入2个点,输出它们的最近公共祖先. 2.裸的最近公共祖先. 3. dfs+ST在线算法: /* LCA(POJ 1330) 在线算法 DFS+ST ...

  3. hdu2586 lca倍增法

    倍增法加了边的权值,bfs的时候顺便把每个点深度求出来即可 #include<iostream> #include<cstring> #include<cstdio> ...

  4. poj1470 LCA倍增法

    倍增法模板题 #include<iostream> #include<cstring> #include<cstdio> #include<queue> ...

  5. 最近公共祖先 LCA 倍增法

    [简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...

  6. luogu3379 【模板】最近公共祖先(LCA) 倍增法

    题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...

  7. LCA—倍增法求解

    LCA(Least Common Ancestors) 即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 常见解法一般有三种 这里讲解一种在线算法-倍增 首先我们定义fa[u][j ...

  8. LCA - 倍增法去求第几个节点

    You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, ...

  9. POJ 1330(LCA/倍增法模板)

    链接:http://poj.org/problem?id=1330 题意:q次询问求两个点u,v的LCA 思路:LCA模板题,首先找一下树的根,然后dfs预处理求LCA(u,v) AC代码: #inc ...

  10. 【模板】Lca倍增法

    Codevs 1036 商务旅行 #include<cstdio> #include<cmath> #include<algorithm> using namesp ...

随机推荐

  1. Web前端开发必备

    前端学习相关书籍 关于书籍 HTML.CSS 类别书籍,都是大同小异,在当当网.卓越网搜索一下很多推荐.如果感觉学的差不多了,可以关注一下<CSS禅意花园>,这个很有影响力. Javasc ...

  2. jQuery 对AMD的支持(Require.js中如何使用jQuery)

    AMD 模块 AMD(异步模块定义,Asynchronous Module Definition)格式总体的目标是为现在的开发者提供一个可用的模块化 JavaScript 的解决方案. AMD 模块格 ...

  3. Mobius反演的套路

    T1 \(\sum_{i=1}^N \sum_{j=1}^M [(i,j)=1]\) \(f(d)=\sum_{i=1}^N \sum_{j=1}^M [(i,j)=d]\) \(g(d)=\sum_ ...

  4. Python异常处理机制、调试、测试

    类似于Java的try..catch..finally Java的为try_except_finally try: print('try...') r = / print('result:', r) ...

  5. Android--记录莫名其妙的引用、依赖冲突解决办法

    这几天一直在忙于其他项目,打开之前的项目,我曹,代码什么都没动,为何莫名其妙的的就出现冲突了.(我猜测是依赖的其它三方库,库里面的依赖发生改变导致的) 全是这种错误,各种冲突导致的,记录一下,以下指示 ...

  6. phprpc的使用示例以及报错Fatal error: Cannot redeclare gzdecode() in D:\wamp\www\immoc\phprpc\compat.php 处理

    今天看书,发现了PHPRPC这个好东东,故在此写下来以作笔记. PHPRPC 是一个轻型的.安全的.跨网际的.跨语言的.跨平台的.跨环境的.跨域的.支持复杂对象传输的.支持引用参数传递的.支持内容输出 ...

  7. php防范

    针对 PHP 的网站主要存在下面几种攻击方式: 1.命令注入(Command Injection) 2.eval 注入(Eval Injection) 3.客户端脚本攻击(Script Inserti ...

  8. vue使用JS的形式进行路由导航

    // 注意: 一定要区分 this.$route 和 this.$router 这两个对象, // 其中: this.$route 是路由[参数对象],所有路由中的参数, params, query ...

  9. C#基础知识之Sender

    /// <summary> /// sender就是事件发起者,e存储事件发起者的一些参数 /// 例如: /// private void button1_Click(object se ...

  10. wrk 压力测试 http benchmark POST接口

    简单的 http 性能测试工具 wrk.git 一个简单的 http benchmark 工具, 能做很多基本的 http 性能测试. wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原 ...