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序

 vector<int>v[maxn];
//node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置
int dep[ * maxn], node[ * maxn], firs[maxn], vis[maxn];
int p = ;
void dfs(int s, int d) //s用来构造dfs序,d用来构造深度序列
{
vis[s] = ;
dep[p] = d; node[p] = s; firs[s] = p++;
for(int i = ; i < v[s].size(); ++i)
{
if(!vis[v[s][i]])
{
dfs(v[s][i], d + ); //孩子的深度肯定是父亲的深度+1
dep[p] = d; node[p++] = s;
}
}
}

然后考一个RMQ板子就行了

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 5e5 + ;
int n, m, s;
vector<int>v[maxn];
//node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置
int dep[ * maxn], node[ * maxn], firs[maxn], vis[maxn];
int p = ;
void dfs(int s, int d) //s用来构造dfs序,d用来构造深度序列
{
vis[s] = ;
dep[p] = d; node[p] = s; firs[s] = p++;
for(int i = ; i < v[s].size(); ++i)
{
if(!vis[v[s][i]])
{
dfs(v[s][i], d + ); //孩子的深度肯定是父亲的深度+1
dep[p] = d; node[p++] = s;
}
}
}
//pos[L][R]表示的是区间[L, R]中最小值在dfs序中的位置
int dp[ * maxn][], pos[ * maxn][], que[ * maxn];
void init()
{
int n = p - ;
for(int i = ; i <= n; ++i) {dp[i][] = dep[i]; pos[i][] = i;}
for(int j = ; ( << j) <= n; ++j)
for(int i = ; i + ( << j) - <= n; ++i)
{
if(dp[i][j - ] < dp[i + ( << (j - ))][j - ])
{
dp[i][j] = dp[i][j - ]; pos[i][j] = pos[i][j - ];
}
else
{
dp[i][j] = dp[i + ( << (j - ))][j - ]; pos[i][j] = pos[i + ( << (j - ))][j - ];
}
}
int k = ;
for(int i = ; i <= n; ++i)
{
if (( << k) <= i) k++;
que[i] = k - ;
}
}
void query(int L, int R)
{
if (R < L) swap(L, R);
int k = que[R - L + ];
if(dp[L][k] < dp[R - ( << k) + ][k]) printf("%d\n", node[pos[L][k]]);
else printf("%d\n", node[pos[R - ( << k) + ][k]]);
return;
}
int main()
{
scanf("%d%d%d", &n, &m, &s);
for(int i = ; i < n; ++i)
{
int a, b; scanf("%d%d", &a, &b);
v[a].push_back(b); v[b].push_back(a);
}
dfs(s, );
init();
while(m--)
{
int a, b; scanf("%d%d", &a, &b);
query(firs[a], firs[b]);
}
return ;
}

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. Python和Java编程题(二)

    题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 兔子的规律为数列1,1,2,3,5,8,13,21 ...

  2. 《Visual C# 从零开始学》

    书名 <Visual C# 从零开始学> 图片 时间 2017年4月-5月 学习 对c#的基础语法有了一个较为完全的了解,总体还算顺利没有遇到理解不了的,感觉最好上手的是做windos窗体 ...

  3. 在C语言结构体中添加成员函数

    我们在使用C语言的结构体时,经常都是只定义几个成员变量,而学过面向对象的人应该知道,我们定义类时,不只是定义了成员变量,还定义了成员方法,而类的结构和结构体非常的相似,所以,为什么不想想如何在C语言结 ...

  4. JS处理数组内如果相同ID追加一个属性(如字体颜色)

    var arr=[{id:0},{id:0},{id:3},{id:2},{id:0},{id:4},{id:0},{id:1},{id:1},{id:2},{id:2}]; for(var i=0; ...

  5. NIO学习笔记二

    Java NIO的通道channel 既可以从通道中读取数据,又可以写数据到通道.通道可以异步地读写.通道中的数据总是要先读到一个Buffer(缓冲区),或者总是要从一个Buffer(缓冲区)中写入. ...

  6. 微信服务号获取openid方法

    public function tetst(){ if(!isset($_GET['code'])){ $APPID = $this->app_id; $ran = rand(1,100); / ...

  7. 前端整理——Vue部分

    (1)Vue的生命周期 1)创建vue实例,初始化生命周期钩子函数 2)数据检测及方法和计算属性代理.在数据检测和初始化数据之前调用beforeCreated(),这时还获取不到props或者data ...

  8. python之装饰器(函数)

    1. 装饰器 遵循的原则: 开闭原则:   对功能的扩展开放 对代码的修改是封闭 # 通用装饰器写法 # 存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能. def wrapp ...

  9. (网页)Java程序员们最常犯的10个错误(转)

    转自CSDN: 1.将数组转化为列表 将数组转化为一个列表时,程序员们经常这样做: List<String> list = Arrays.asList(arr); Arrays.asLis ...

  10. (后端)Java新人入职——配置环境及安装开发工具(完全)

    转自csdn:执笔记忆的空白 很多新人对于进入新公司,相关工具的安装和环境变量的设定很苦恼.又苦于没有完整的配置开发环境的资料,我这里写一篇操作步骤的案例, 至少让你能把开发工具安装起来,并实用起来, ...