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]

  1. void dfs(int u,int pa)
  2. {
  3. dep[u]=dep[pa]+1;
  4. fa[u][0]=pa;
  5. for(int i=head[u];i;i=E[i].nxt)
  6. {
  7. int v=E[i].v;
  8. if(v!=pa) dfs(v,u);
  9. }
  10. }

预处理fa数组

  1. for(int i=1;(1<<i)<=n;i++)
  2. for(int u=1;u<=n;u++)
  3. fa[u][i]=fa[ fa[u][i-1] ][i-1];

预处理之后怎么求解LCA(u,v)呢

我么先假定dep[u]>dep[v]

则两点深度差 d=dep[u]-dep[v]

现在我们要做的是把u升到与v同样的深度

怎么做呢? 先贴代码

  1. for(int i=0;(1<<i)<=d;i++)
  2. 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号位)

  1. if( (1<<i) & d )

这一段用来检查d的二进制下第i位是否为1


抬升到相同高度后就可以开始查询LCA了

同样先上代码

  1. for(int i=(int)log(n);i>=0;i--)
  2. {
  3. if(fa[x][i]!=fa[y][i])
  4. {
  5. x=fa[x][i];
  6. y=fa[y][i];
  7. }
  8. }
  9. return fa[x][0];
  10. }

大体思路就是

从u和v最远的祖先开始

如果u的第2^i祖先等于 v的第2^i祖先,就不移动

否则u和v同时上移2^i个深度

最后u的父亲一定是u和v的LCA

(其实蒟蒻不是特别理解其中的玄学道理,但手动模拟了几组发现真的是这样)

妙啊~妙啊~


最后贴上一份完整代码

模板题传送门啦~啦~啦~

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. #include<queue>
  6. #include<cstring>
  7. #include<cmath>
  8. using namespace std;
  9. int read()
  10. {
  11. int f=1,x=0;
  12. char ss=getchar();
  13. while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
  14. while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
  15. return x*f;
  16. }
  17. void print(int x)
  18. {
  19. if(x<0){putchar('-');x=-x;}
  20. if(x>9) print(x/10);
  21. putchar(x%10+'0');
  22. }
  23. int n,m,s;
  24. int tot;
  25. struct node{int v,nxt;}E[1000010];
  26. int head[1000010];
  27. int fa[5000010][25];
  28. int dep[1000010];
  29. void add(int u,int v)
  30. {
  31. E[++tot].nxt=head[u];
  32. E[tot].v=v;
  33. head[u]=tot;
  34. }
  35. void dfs(int u,int pa)
  36. {
  37. dep[u]=dep[pa]+1;
  38. fa[u][0]=pa;
  39. for(int i=head[u];i;i=E[i].nxt)
  40. {
  41. int v=E[i].v;
  42. if(v!=pa) dfs(v,u);
  43. }
  44. }
  45. int lca(int x,int y)
  46. {
  47. if(dep[x]-dep[y]<0) swap(x,y);//将u设为深度较深的结点
  48. int d=dep[x]-dep[y];
  49. for(int i=0;(1<<i)<=d;i++)
  50. if( (1<<i) & d ) x=fa[x][i];//抬升
  51. if(x==y) return x;//抬升后x==y,则其LCA就是y(或此时的x)
  52. else
  53. {
  54. for(int i=(int)log(n);i>=0;i--)
  55. {
  56. if(fa[x][i]!=fa[y][i])
  57. {
  58. x=fa[x][i];
  59. y=fa[y][i];
  60. }
  61. }
  62. return fa[x][0];
  63. }
  64. }
  65. int main()
  66. {
  67. n=read();m=read();s=read();
  68. for(int i=1;i<=n-1;i++)
  69. {
  70. int x=read(),y=read();
  71. add(x,y);add(y,x);
  72. }
  73. dfs(s,-1);
  74. //无根树转有根树,在这里调用是pa要设为-1
  75. //预处理fa数组
  76. for(int i=1;(1<<i)<=n;i++)
  77. for(int u=1;u<=n;u++)
  78. fa[u][i]=fa[ fa[u][i-1] ][i-1];
  79. while(m--)
  80. {
  81. int x=read(),y=read();
  82. int ans=lca(x,y);
  83. print(ans); printf("\n");
  84. }
  85. return 0;
  86. }

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. 浅谈倍增法求解LCA

    Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...

  4. hdu2586 lca倍增法

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

  5. poj1470 LCA倍增法

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

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

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

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

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

  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 ...

随机推荐

  1. dedecms_

    2012-7-5(no1)当我们点击检索结果的某个电影超链接时,如何跳转到对应的内容页[本资源由www.qinglongweb.com搜集整理] dedelist标签 --可以嵌套 项目移植: mys ...

  2. Java 通过Xml导出Excel文件,Java Excel 导出工具类,Java导出Excel工具类

    Java 通过Xml导出Excel文件,Java Excel 导出工具类,Java导出Excel工具类 ============================== ©Copyright 蕃薯耀 20 ...

  3. 信号处理引发的cpu高

    背景知识: 1.tty 终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备. tty指的是七个alt+crtl+F1~F7.tty1-tty6表示文字界面,可以用Ctrl+Al ...

  4. ASP.NET MVC5 中百度ueditor富文本编辑器的使用

    随着网站信息发布内容越来越多,越来越重视美观,富文本编辑就是不可缺少的了,众多编辑器比较后我选了百度的ueditor富文本编辑器. 百度ueditor富文本编辑器分为两种一种是完全版的ueditor, ...

  5. JVM-类的四种载入方式

    package org.burning.sport.javase.classloader; public class ClassLoaderMain { public static void main ...

  6. sql 查询 ORA-12170 TNS 连接超时特殊原因

    一般对于ORA-12170这个问题 1 看数据ip 是否能ping通 2 数据库服务是否启动 3 数据库服务所在服务器防火墙 然而当你能进去数据,而报这个这错时: 1 看代码中数据库连接是写错 2 如 ...

  7. scss 初学笔记 三 继承

    //继承 .btn{ padding: 4px 10px; font-size: 14px; } .primary{ background:red; @extend .btn; } //%placeh ...

  8. linux_发邮件

    如何通过linux发邮件? 邮箱配置文件: /etc/mail.rc 1. 邮箱文件配置 vim /etc/mail.rc # 添加一下数据 set from=beimen@163.com smtp= ...

  9. Node.js之单利模式

    在iOS中我们经常用到单利模式,这样就能够实现在多处共享同一数据,那么在Node.js中也存在这种模式. 我们知道,Node.js中模块的概念很重要,在写模块的接口的时候,只需要暴露出一个实例对象就能 ...

  10. redis集群主从集群搭建、sentinel(哨兵集群)配置以及Jedis 哨兵模式简要配置

    前端时间项目上为了提高平台性能,为应用添加了redis缓存,为了提高服务的可靠性,redis部署了高可用的主从缓存,主从切换使用的redis自带的sentinel集群.现在权作记录.