ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

倍增求lCA

2021-02-21 10:02:23  阅读:134  来源: 互联网

标签:结点 int 代码 lCA dfs 祖先 LCA 倍增


什么是LCA

LCA(Least Common Ancestors),有一棵树,若结点z既是结点x的祖先也是结点y的祖先,且z的深度最大,那么称这个结点x是结点a,b的最近公共祖先。

倍增法是什么

其实求LCA,有一个极为朴素的方法,就是先比较两个结点的深度,然后dfs大的直到深度和小的一样,然后这两个结点一起dfs,直到找到同一个结点,而倍增则是利用了二进制拆分,将范围扩大一倍,从而加速操作,这个思想和st表很像,也要先预处理出一个表。

求\(f\)数组

我们需要预处理出一个数组\(f[i][j]\) 表示i的\(2^j\)辈的祖先,也就是x向根节点走\(2^j\)步到的结点,一般来说\(2^{20}\)就已经够了,我们可以用状态转移方程:\(f[i][j]=f[f[i][j-1]][j-1]\) 来求得f数组,我们可以推导一下如何求出这个方程:

特别地,如果这个结点的2^j辈没有结点那那么我们让\(f[i][j]=0\) ,\(f[i][0]\) 就是i结点的父亲,因为对于一个结点,它走\(2^j\)步,其实等同于它先走\(2^{j-1}\),再走\(2^{j-1}\)步,这样我们就推出了\(f[i][j]=f[f[i][j-1]][j-1]\)其中\(f[i][j-1]\)表示\(i\)的\(2^{j-1}\)辈祖先的位置,后面的\([j-1]\)可以认为让\(2^{j-1}\)辈祖先,走\(2^{j-1}\)步,这个方程很好理解,所以不需要去死记硬背。

dfs求深度,求f数组的代码

dfs(s,0);//s表示根节点的序号
void dfs(int u,int fa){//其实这个dfs真正要求的是当前结点的深度
  f[u][0]=fa;
  for(int i=1;i<=20;i++)
    f[u][i]=f[f[u][i-1]][i-1];//预处理当前结点的祖先
  dep[u]=dep[fa]+1;//深度会等于父亲结点+1
  for(int i=head[u];i;i=net[i]){//邻接表
    int v=ver[i];
    if(v==fa) continue;//对于双向边来说肯定有可能出现儿子走向父亲的边
    dfs(v,u);
}

求祖先

我们要知道的一点是,先走大步再走小步,为什么呢?我们可以这样想,如果先走小步,走到后面发现不需要走这一步这么办?那就只能回溯了,我们还可以换一种理解,我们在往箱子里塞东西时,肯定会先塞大的东西,然后用小的东西逐渐填满空隙。

Lca的步骤

  1. 我们先判断两个结点的深度,然后不断让更深的结点走,直到两个结点很接近了,这个思想其实和朴素求LCA是一样的。
  2. 让两个结点一起跳,知道他们的父亲相同

lca的代码

int lca(int x,int y){
  if(dep[x]<dep[y]) swap(x,y);//保证x更深
  for(int i=20;i>=0;i--)//逆序
    if(dep[f[x][i]]>=dep[y])//不断让这两个点接近,注意点1
      x=f[x][i];
  if(x==y) return x;//有可能这两个结点一开始就是祖辈关系
  for(int i=20;i>=0;i--)
    if(f[x][i]!=f[y][i]){//注意点2
      x=f[x][i];
      y=f[y][i];
    }
  return f[x][0];
}

注意

在上面的代码,标注了两个要注意的地方:

  1. 为什么是大于或等于,因为当出现等于的情况时就找到LCA了
  2. 这段代码可能会有些疑惑:“咦,上段代码都可以等于,为什么这段代码就不行呢(⊙o⊙)?”因为我们要求的是最近公共祖先。

标签:结点,int,代码,lCA,dfs,祖先,LCA,倍增
来源: https://www.cnblogs.com/hhhd-/p/14424263.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有