LCA(Lowest Common Ancestor 最近公共祖先)定义如下:在一棵树中两个节点的LCA为这两个节点所有的公共祖先中深度最大的节点。

比如这棵树

结点5和6的LCA是2,12和7的LCA是1,8和14的LCA是4。

这里讲一下将LCA转化成RMQ问题,进而用st表求解。

首先我们跑一遍dfs,并记录经过的每一个结点(包括回溯的时候),存到一个数组中,这样我就将树的问题转化成线性问题。

等下上图的树好像有些大

这就好多了。

然后就是dfs序列和深度序列

dfs序   1  2  5  2  6  2  1  3  1  4  1

dep序  1  2  3  2  3  2  1  2  1  2  1

根据dfs的性质,从node[u]遍历到node[v]的过程中,经过LCA(u, v)有且仅有一次,而且它的深度一定是区间[u, v]中是最小的,那么这就是一个显而易见的RMQ(静态区间最小值)问题了。

我们就以洛谷的板子为例,传送门:https://www.luogu.org/problemnew/show/P3379

这里的vis数组是用来处理无向图存两次边的

先写一个dfs序

  1. vector<int>v[maxn];
  2. //node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置
  3. int dep[ * maxn], node[ * maxn], firs[maxn], vis[maxn];
  4. int p = ;
  5. void dfs(int s, int d) //s用来构造dfs序,d用来构造深度序列
  6. {
  7. vis[s] = ;
  8. dep[p] = d; node[p] = s; firs[s] = p++;
  9. for(int i = ; i < v[s].size(); ++i)
  10. {
  11. if(!vis[v[s][i]])
  12. {
  13. dfs(v[s][i], d + ); //孩子的深度肯定是父亲的深度+1
  14. dep[p] = d; node[p++] = s;
  15. }
  16. }
  17. }

然后考一个RMQ板子就行了

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstring>
  6. #include<vector>
  7. using namespace std;
  8. const int maxn = 5e5 + ;
  9. int n, m, s;
  10. vector<int>v[maxn];
  11. //node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置
  12. int dep[ * maxn], node[ * maxn], firs[maxn], vis[maxn];
  13. int p = ;
  14. void dfs(int s, int d) //s用来构造dfs序,d用来构造深度序列
  15. {
  16. vis[s] = ;
  17. dep[p] = d; node[p] = s; firs[s] = p++;
  18. for(int i = ; i < v[s].size(); ++i)
  19. {
  20. if(!vis[v[s][i]])
  21. {
  22. dfs(v[s][i], d + ); //孩子的深度肯定是父亲的深度+1
  23. dep[p] = d; node[p++] = s;
  24. }
  25. }
  26. }
  27. //pos[L][R]表示的是区间[L, R]中最小值在dfs序中的位置
  28. int dp[ * maxn][], pos[ * maxn][], que[ * maxn];
  29. void init()
  30. {
  31. int n = p - ;
  32. for(int i = ; i <= n; ++i) {dp[i][] = dep[i]; pos[i][] = i;}
  33. for(int j = ; ( << j) <= n; ++j)
  34. for(int i = ; i + ( << j) - <= n; ++i)
  35. {
  36. if(dp[i][j - ] < dp[i + ( << (j - ))][j - ])
  37. {
  38. dp[i][j] = dp[i][j - ]; pos[i][j] = pos[i][j - ];
  39. }
  40. else
  41. {
  42. dp[i][j] = dp[i + ( << (j - ))][j - ]; pos[i][j] = pos[i + ( << (j - ))][j - ];
  43. }
  44. }
  45. int k = ;
  46. for(int i = ; i <= n; ++i)
  47. {
  48. if (( << k) <= i) k++;
  49. que[i] = k - ;
  50. }
  51. }
  52. void query(int L, int R)
  53. {
  54. if (R < L) swap(L, R);
  55. int k = que[R - L + ];
  56. if(dp[L][k] < dp[R - ( << k) + ][k]) printf("%d\n", node[pos[L][k]]);
  57. else printf("%d\n", node[pos[R - ( << k) + ][k]]);
  58. return;
  59. }
  60. int main()
  61. {
  62. scanf("%d%d%d", &n, &m, &s);
  63. for(int i = ; i < n; ++i)
  64. {
  65. int a, b; scanf("%d%d", &a, &b);
  66. v[a].push_back(b); v[b].push_back(a);
  67. }
  68. dfs(s, );
  69. init();
  70. while(m--)
  71. {
  72. int a, b; scanf("%d%d", &a, &b);
  73. query(firs[a], firs[b]);
  74. }
  75. return ;
  76. }

LCA转换成RMQ的更多相关文章

  1. DataTable 转换成 Json的3种方法

    在web开发中,我们可能会有这样的需求,为了便于前台的JS的处理,我们需要将查询出的数据源格式比如:List<T>.DataTable转换为Json格式.特别在使用Extjs框架的时候,A ...

  2. 微信小程序中利用时间选择器和js无计算实现定时器(将字符串或秒数转换成倒计时)

    转载注明出处 改成了一个单独的js文件,并修改代码增加了通用性,点击这里查看 今天写小程序,有一个需求就是用户选择时间,然后我这边就要开始倒计时. 因为小程序的限制,所以直接选用时间选择器作为选择定时 ...

  3. C#将Word转换成PDF方法总结(基于Office和WPS两种方案)

    有时候,我们需要在线上预览word文档,当然我们可以用NPOI抽出Word中的文字和表格,然后显示到网页上面,但是这样会丢失掉Word中原有的格式和图片.一个比较好的办法就是将word转换成pdf,然 ...

  4. DataTable转换成IList<T>的简单实现

    DataTable的无奈 很多时候,我们需要去操作DataTable.但DataTable的操作,实在是太不方便了.Linq?lambda表达式?统统没有... 特别是对现有结果集做进一步筛选,这样的 ...

  5. ASP.Net中实现上传过程中将文本文件转换成PDF的方法

    iTextSharp是一个常用的PDF库,我们可以使用它来创建.修改PDF文件或对PDF文件进行一些其他额外的操作.本文讲述了如何在上传过程中将文本文件转换成PDF的方法. 基本工作 在开始之前,我们 ...

  6. asp.net dataTable转换成Json格式

    /// <summary> /// dataTable转换成Json格式 /// </summary> /// <param name="dt"> ...

  7. [jquery]将当前时间转换成yyyymmdd格式

    如题: function nowtime(){//将当前时间转换成yyyymmdd格式 var mydate = new Date(); var str = "" + mydate ...

  8. Gson将字符串转换成JsonObject和JsonArray

    以下均利用Gson来处理: 1.将bean转换成Json字符串: public static String beanToJSONString(Object bean) { return new Gso ...

  9. JAVA将数字字符串强制转换成整型变量----求参数之和实验代码(附流程图)

    一.设计思想 先将参数个数输出,并利用循环结果将参数逐个输出,再将字符串强制转化成整型,利用循环结构相加求和 二.程序流程图 三.源程序代码 package demo; public class Co ...

随机推荐

  1. IdentityServer4 中文文档 -6- (简介)示例服务器和测试

    IdentityServer4 中文文档 -6- (简介)示例服务器和测试 原文:http://docs.identityserver.io/en/release/intro/test.html 目 ...

  2. int和Integer有什么区别?

    Java提供两种不同的类型:引用类型和原始类型(或内置类型): int是Java的原始数据类型,Integer是java为int提供的封装类. java为每个原始类型提供了封装类: 原始类型:bool ...

  3. webpack 学习总结demo

    github源码地址 https://github.com/ghshuo/webpack-demo webpack介绍 webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo ...

  4. SqlServer主键

    *主键 作用:唯一标识表中的一条记录. *特点: 1不能重复的列. 2主键不能为null. *同名时如何处理:王洋(大) 王洋(小) *主键有两种选用策略: 业务主键和逻辑主键. 业务主键是使用有业务 ...

  5. Ubuntu下JDK1.8安装后配置环境变量

    export JAVA_HOME=/dengyang/jdk1.8.0_56export JRE_HOME=$JAVA_HOME/jreexport CLASSPATH=.:$JAVA_HOME/li ...

  6. 【Dubbo&&Zookeeper】3、Failed to read schema document 'http://code.alibabatech.com/schema/dubbo/dubbo.xsd'问题解决方法

    转自:http://blog.csdn.net/gaoshanliushui2009/article/details/50469595 我们公司使了阿里的dubbo,但是阿里的开源网站http://c ...

  7. C#特性之数据类型

    这篇文章主要通过演示类在不同发展中的不通过定义方法,来向读者表述它们之间的区别和联系. 在C#1时代,我们喜欢这样定义类: public class Product { private string ...

  8. 迭代器模式(Iterator)

    1.概念 迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示,属于行为模式的一种 2.模式结构 抽象迭代器(Iterator):此抽象角色定义出遍历元素所需的接口 具体 ...

  9. vue选中与取消简单实现

    <li v-for="(item,index) in assign" :key="index" @click="selected(item)&q ...

  10. PHP与.Net的区别(一)接口

    一.关于接口成员 PHP的接口成员只能包括两种: 1.函数签名 2.常量 .Net的接口成员只能包括三种: 1.函数签名 2.属性(注意:是属性,不是字段) 3.事件 4.索引器(也叫有参属性)