LCA 最近公共祖先 (倍增算法)


首先了解一下我们 最近公共祖先
E和G的LCA为A
L和J的LCA为D
K和F的LCA为B
然后 倍增 用到了二进制和 dp 的思想
倍增 就是 1 2 4 8 16 …任何一个数 都是可以右 这些数相加得到的。(了解一下二进制)
首先 定义:

fa[ i ] [ j ] 为 从 i 节点 向上走 2 ^ j 个节点,
depth[ i ] 为节点 i 的深度。
举个例子 fa[ k ][ 1 ]=B , fa[ i ][ 0 ]=D ;
depth[ H ]=3,depth[ A ]=1;

定义完这个 我们就可以通过 一个 dfs 把 每个节点的深度 和 每个节点向上走 1 步 都求出来
代码

void dfs(int u,int pre,int d){     //u为子节点 pre为父节点fa[u][0]=pre;depth[u]=d;for(int i=head[u];i!=-1;i=e[i].next){int v=e[i].to;if(v==pre) continue;//dis[v]=dis[u]+e[i].w; 这里是求两节点之间距离的时候用的。(记录每一个节点 到根节点的距离)dfs(v,u,d+1);}
}

ps :当时听学长讲的时候有点懵 不是求公共祖先吗 这个有什么用呢 到后面发现挺神奇的

接下来 我们就要想办法把 每个节点向上走 n 步 给表示出来

前面说过 1 2 4 8 16 … 这些二进制数是可以把所有的数都表示出来的 所以我们只要 把每一个节点向上走 2 ^ n 表示出来 就可以把走任意步都表示出来了。
考虑一下 我们可以发现
fa[ i ] [ j ]=fa[ fa[ i ] [ j-1 ] ][ j-1 ] 。
就是 说 如果我们要求 i 向上走 8 不步 可以由 i 先走 4 步 再 走 4 步 得到。 我们可以用 dp 实现这个步骤
代码

void init(int root){dfs(root, -1,1);for(int j=1;(1 << j )<n;j++){              for(int i=1;i<=n;i++){if(fa[i][j-1]<0) fa[i][j]=-1; //如果超过了根节点 让它等于 -1;else fa[i][j]=fa[fa[i][j-1]][j-1];}}
}

做完这些预处理 接下来就是找最近公共祖先的步骤了

ps 四处盗图 哈哈

首先 我们让 所求的两个节点处于 同一深度 然后一起向上跳就行了

至于怎么跳 就比较关键了
最简单的方法 就是一个点一个点的跳直到 到达相同的节点(这样肯定会超时的。。。。。。。)

所以换一种思路 就是 这种方法的核心了

像 上图的 15 16 这两个节点 处于同一深度
我们要想办法 让他们 跳到 最近公共祖先的下面一个节点

采取 这样的 策略 从 2 ^ k到 2^0 步进行枚举

  1. 如果 他们跳了 2^k 步之后 到达了 同一个节点 说明 我们到达了 最近公共祖先 或者 以上的节点 不符合我们的要求 (不跳)。
  2. 过程中如果 碰到跳完后到达了不同的节点 例如 2^0 我们就让他跳 (可以去找个大一点的树模拟一下)
    最后 我们会神奇的发现 他们 来到了 6 和 10 这两个节点
    所以 最近公共祖先就为 fa[ 6 ][0 ];

是不是很神奇 (我学的时候感觉挺神奇的)

代码:

int lca(int u,int v){if(depth[u]<depth[v]) swap(u,v);int k=depth[u]-depth[v];for(int i=0;(1<<i)<=k;i++){if((k>>i)&1) u=fa[u][i];  //这里是 判断K的 第i 个二进制位是否为 1 是的话 就跳}if(u==v) return u;for(int i=log(n*1.0)/log(2.0);i>=0;i--){if(fa[u][i]!=fa[v][i]){u=fa[u][i];v=fa[v][i];}}return fa[u][0];
}

来个 题目
凛冬将至 (CSUST 2019年集训队选拔赛)
Description

维斯特洛大陆的原住民是森林之子,他们长得如孩童一般,善于使用石器,威力值35,用树叶树枝作为衣物,在森林里繁衍生息,与万物和平相处。他们会使用古老的魔法(比如绿之视野),威力值55。后来先民从维斯特洛大陆架登陆,凭借手中的青铜兵器和战马大举入侵,威力值分别是35和55。森林之子凭借魔法顽强抵抗,并冒险利用龙晶制造出了一个神奇的强悍的物种——异鬼,威力值60。双方持久不下之时签订了和平协议,先民占据了维斯特洛大陆,森林之子只保有森林。

。。。

七大王国如火如荼兴起之时,在遥远海洋的另一端,一个神秘的家族悄然兴起——坦格利安家族。此家族拥有三条巨龙,威力值90+,经过一个世纪的备战,在领导者伊耿一世的带领下乘龙入侵维斯特洛大陆。

借助龙的力量,伊耿一世很快统一了维斯特洛的七大王国,建立了空前强大的坦格利安王朝,像所有外来入侵者一样,坦格利安家族摒弃了龙的信仰开始信仰七神,并且将龙由放养改为圈养,再加上坦格利安家族为了保持血统纯正,实行近亲婚姻,生出来的继承者精神病人越来越多,这导让坦格利安王朝开始了眼花缭乱的花样作死之旅。

。。。

众(wo)所(xia)周(che)知(de),当凯特琳·徒利得知自己女儿艾莉亚逃到赫伦堡后,非常担心女儿的安全。假设维斯特洛大陆共有nn个城市,共有n-1n−1条双向道路把这nn个城市连接起来。也就是说这是一棵树。凯特琳想尽快临冬城赶到赫伦堡。除了已知的n-1n−1条边外,凯特琳还知道一条额外的秘密路径(也是双向的):端点是是城市xx和城市yy,路径长度是zz。现在想考考寒假过后的你有没有刷过题,问你QQ个问题,每个问题给出临冬城(凯特琳所在城市)和赫伦堡(艾莉亚所在城市)的坐标,请你告诉凯特琳从临冬城到赫伦堡的最短路径长度是多少?

Input
第一行一个整数n(1<=n<=100000)

以下n-1行描述一颗树,每行u,v,w表示一条从u到v长为w的路径,u!=v。

下一行三个整数x,y,z 意义如题(1<= x,y<=n, x!=y)。

下一行一个整数Q(100000)

以下Q行两个数字U,V代表临冬城和赫伦堡的坐标。

1≤w,z≤1000

Output
对每次询问输出从临冬城到赫伦堡的最短路径长度。

代码

#include<iostream>
#include<cstdio>
#include<map>
#include<math.h>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6;
const int mod=1e9+7;
int head[maxn],num=0,depth[maxn],fa[maxn][32],n,vis[maxn],dis[maxn];
struct node{int to,next,w;
}e[maxn];
void add(int u,int v,int w){e[num].next=head[u];e[num].to=v;e[num].w=w;head[u]=num++;
}
void dfs(int u,int pre,int d){fa[u][0]=pre;depth[u]=d;for(int i=head[u];i!=-1;i=e[i].next){int v=e[i].to;if(v==pre) continue;dis[v]=dis[u]+e[i].w;dfs(v,u,d+1);}
}
void init(int root){dfs(root, -1,1);for(int j=1;(1 << j )<n;j++){              //这两个循环  重要for(int i=1;i<=n;i++){if(fa[i][j-1]<0) fa[i][j]=-1;else fa[i][j]=fa[fa[i][j-1]][j-1];}}
}
int lca(int u,int v){if(depth[u]<depth[v]) swap(u,v);int k=depth[u]-depth[v];for(int i=0;(1<<i)<=k;i++){if((k>>i)&1) u=fa[u][i];}if(u==v) return u;for(int i=log(n*1.0)/log(2.0);i>=0;i--){if(fa[u][i]!=fa[v][i]){u=fa[u][i];v=fa[v][i];}}return fa[u][0];
}
int d(int u,int v){return dis[u]+dis[v]-2*dis[lca(u,v)];
}
int main (){int u,v,w,root,x,y,z;cin>>n;memset(head,-1,sizeof(head));for(int i=0;i<n-1;i++){scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,w);vis[v]++;}scanf("%d%d%d",&x,&y,&z);for(int i=1;i<=n;i++){if(vis[i]==0){root=i;break;}}init(root);int k;cin>>k;while(k--){int ans=1000000000;scanf("%d%d",&u,&v);ans=min(ans,d(u,v));ans=min(ans,d(u,x)+z+d(y,v));ans=min(ans,d(u,y)+z+d(x,v));printf ("%d\n",ans);}
}

u 到 v 的距离 为 dis[u]+dis[v]-2*dis[lca(u,v)];
dis[]为 到根节点的距离.
ps: 纯萌新 写的笔记 写得比较烂 欢迎指出错误

LCA 最近公共祖先 (倍增算法)相关推荐

  1. tarjan算法_【朝夕的ACM笔记】树上问题-最近公共祖先-倍增算法

    [朝夕的ACM笔记]目录与索引 最近公共祖先-倍增算法 一.基本概念 最近公共祖先问题:对于给定的一颗有根树,求其两个节点的最近公共祖先. 祖先:节点本身.节点的父亲.节点父亲的父亲--都是该节点的祖 ...

  2. 例题 - 最近公共祖先 - 离线算法

    贴几篇LCA知识点的博客详解: CSDN - 青烟绕指柔!的博客 - 倍增算法 CSDN - Nekroz_的博客 CSDN - Dust_Heart的博客 CSDN - creatorx的博客 -T ...

  3. LCA(最近公共祖先)问题

    1.这个算法基于并查集和深度优先搜索.算法从根开始,对每一棵子树进行深度优先搜索,访问根时,将创建由根结点构建的集合,然后对以他的孩子结点为根的子树进行搜索,使对于 u, v 属于其某一棵子树的 LC ...

  4. LCA 最近公共祖先(RMQ、树上倍增、Tarjan),树上两点距离,线段重合长度

    对于LCA的一些理解 RMQ dfs处理树 对于一个树形结构,可以用dfs将一颗树转化成数组,数组中记录每个点的标号,这样数组就按照dfs的顺序把树存了下来 确定祖先的范围 对于询问的节点X和Y, X ...

  5. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)...

    转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2194090a96bbed2db1351de8.html 基本概念: 1.割点:若删掉某点后,原连通图 ...

  6. 模板 - LCA最近公共祖先(倍增法、Tarjan、树上差分、LCA优化的次小生成树)

    整理的算法模板合集: ACM模板 注意x和y的LCA可以是x或者y本身 一.LCA的在线倍增算法 /*给定一棵包含 n个节点的有根无向树,有 m个询问,每个询问 给出了一对节点的编号 x和 y,询问 ...

  7. POJ 1330 LCA最近公共祖先 离线tarjan算法

    题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集 ...

  8. 故事篇之 LCA 最近公共祖先(一)

    故事出真知 阿珍 爱上了 阿强 但 被他们的父母 拒绝 了 没错 狗血的近亲结婚剧情开始了 阿珍 说: 不行,我深深爱着我的阿强,谁也不能把我们分开 阿强 深情地望着阿珍 说 一定会有机会的! 此时 ...

  9. LCA 最近公共祖先

    LCA ,也就是最近公共祖先是什么意思呢. 下面这张图可能会让你清楚的明白什么是最近公共祖先. 对于初始点,前提是它能构成一棵不成环的树,之所以不能成环,从定义就看出来了嘛,如果成环,是不是有种1是3 ...

  10. LCA(最近公共祖先)(leetcode 236 python C++)

    LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. # Definition for a binary tree node. ...

最新文章

  1. 【莫队算法】bzoj3781 小B的询问
  2. docker命令大全
  3. mysql第四篇:数据操作之多表查询
  4. IDE-Ecplise-代码注释 模版 编码规范 配色
  5. Visual Basic的未来之路
  6. mysql主从、主主复制及高可用性
  7. mysql oracle linux_mysql、oracle在Linux和Windows下的简单自动备份
  8. spark 数据倾斜之两阶段聚合(局部聚合+全局聚合)
  9. MapGuide open source开发系列教程六: 地图状态与事件(含问题)
  10. git 服务器上新建项目
  11. 驱动程序和应用程序的区别_复仇者黑客组织—教你写第一个Linux设备驱动程序...
  12. 我的小游戏开发之路|腾讯TGideas周桂华(花叔)
  13. HTML+CSS项目实践四:给html网页标题栏添加logo图标(ico格式图片)
  14. 调用marathon rest API
  15. 分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择最佳实践
  16. JVM学习:JVM对象分代晋升机制
  17. rust最美建筑_[资料整理]动物之森的美丽物语 (多图;补完)
  18. 塔望3W消费战略全案|巨头林立,卤味新品牌的破局之路
  19. FOC中电流环调试的宝贵经验总结
  20. Windows入门(一)

热门文章

  1. 基于回声状态网络(ESN)的时间序列预测
  2. 图片文字识别的方法有哪些?
  3. php开发erp思路,ERP遇到业务逻辑问题,求思路
  4. FSA-Net环境配置
  5. 单体架构和微服务架构
  6. jsp页面打开为空白页
  7. 计算机专业公务员三不限,公务员:三不限岗位是大坑,这3类专业最受欢迎,选对才是硬道理...
  8. No symbols loaded
  9. java使用keytool 的ssl证书的导入、查看与删除——彻底解决unable to find valid certification path to requested target(好文章!)
  10. Java 判断是否为大写字母