LCA与RMQ
一、什么是LCA?
LCA:Least Common Ancestors(最近公共祖先),对于一棵有根树T的任意两个节点u,v,求出LCA(T, u, v),即离跟最远的节点x,使得x同时是u和v的祖先。
二、算法分类
求LCA的算法很多,按照是否在线可以分为在线算法和离线算法。
在线算法:用比较长的时间做预处理,但是等信息充足以后每次回答询问只需要用比较少的时间。
离线算法:先把所有的询问读入,然后一起把所有询问回答完成,不是本文所讲,Click here
三、在线算法
(1)什么是RMQ?
RMQ:给出一个数组A,回答询问RMQ(A, i, j),即A[i...j]之间的最值的下标。
(2)RMQ算法,不是本文所讲,Click here
(3)RMQ与LCA
假设一颗有根树,如图所示:

我们可以通过深搜(从1节点开始)得到这样一个序列:
欧拉序列V:1 2 1 3 4 3 5 6 5 7 5 3 1
深度序列D:0 1 0 1 2 1 2 3 2 3 2 1 0
First:1 2 4 5 7 8 10
First表示节点i在欧拉序列V中第一次出现的位置
现在比如我们要求LCA(4,7)
那么找到节点4和7第一次出现的位置即:First(4)=5,First(7)=10
对应到欧拉序列V中区间[5,10]即:4 3 5 6 5 7
发现正好是以3为根的子树,然我我们找到这几个数中深度最小的即D(3),说明3就是4和7的最近公共祖先
同理再比如说要求LCA(2,5),First(2)=2,First(5)=7,找到对应区间[2,7]即2 1 3 4 3 5
然后找到深度最小的即1,说明1就是2和5的最近公共祖先。
四:例题
AC CODE:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define N 40010
#define M N*2 struct edge
{
int u,v,w,next;
}edge[M];
int tot,head[N]; int n,m;
int idx;
bool vis[N];
int ver[*N];
int dep[*N];
int first[N];
int dis[N];
int dp[*N][]; void init()
{
tot=;
memset(head,-,sizeof(head));
}
void add(int u,int v,int w)
{
edge[tot].u=u;
edge[tot].v=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void init2()
{
idx=;
dis[]=;
memset(vis,,sizeof(vis));
}
void dfs(int u,int d)
{
vis[u]=;
ver[++idx]=u;
first[u]=idx;
dep[idx]=d;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(!vis[v]){
int w=edge[i].w;
dis[v]=dis[u]+w;
dfs(v,d+);
ver[++idx]=u;
dep[idx]=d;
}
}
}
void ST(int n)
{
int i,j;
for(i=;i<=n;i++) dp[i][]=i;
for(j=;(<<j)<=n;j++){
for(i=;i<=n;i++){
if(i+(<<j)-<=n){
int a=dp[i][j-];
int b=dp[i+(<<(j-))][j-];
dp[i][j]=dep[a]<dep[b]?a:b;
}
}
}
}
int RMQ(int i,int j)
{
int k=(int)(log((double)(j-i+))/log(2.0));
int a=dp[i][k],b=dp[j-(<<k)+][k];
return dep[a]<dep[b]?a:b;
}
int LCA(int u,int v)
{
int x=first[u];
int y=first[v];
if(x>y) swap(x,y);
int res=RMQ(x,y);
return ver[res];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
init2();
dfs(,);
ST(*n-);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
int lca=LCA(u,v);
printf("%d\n",dis[u]+dis[v]-*dis[lca]);
}
}
return ;
}
LCA与RMQ的更多相关文章
- LCA和RMQ
下面写提供几个学习LCA和RMQ的博客,都很通熟易懂 http://dongxicheng.org/structure/lca-rmq/ 这个应该是讲得最好的,且博主还有很多其他文章,可以读读,感觉认 ...
- ZOJ 3195 Design the city LCA转RMQ
题意:给定n个点,下面n-1行 u , v ,dis 表示一条无向边和边权值,这里给了一颗无向树 下面m表示m个询问,问 u v n 三点最短距离 典型的LCA转RMQ #include<std ...
- [CF 191C]Fools and Roads[LCA Tarjan算法][LCA 与 RMQ问题的转化][LCA ST算法]
参考: 1. 郭华阳 - 算法合集之<RMQ与LCA问题>. 讲得很清楚! 2. http://www.cnblogs.com/lazycal/archive/2012/08/11/263 ...
- lca转RMQ
这个博客写得好 #include <stdio.h> #include <vector> #include <string.h> using namespace s ...
- HDU 3078 LCA转RMQ
题意: n个点 m个询问 下面n个数字表示点权值 n-1行给定一棵树 m个询问 k u v k为0时把u点权值改为v 或者问 u-v的路径上 第k大的数 思路: LCA转RMQ求出 LCA(u,v) ...
- 【51NOD1766】树上的最远点对(线段树,LCA,RMQ)
题意:n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间, 表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c< ...
- POJ 1986(LCA and RMQ)
题意:给定一棵树,求任意两点之间的距离. 思路:由于树的特殊性,所以任意两点之间的路径是唯一的.u到v的距离等于dis(u) + dis(v) - 2 * dis(lca(u, v)); 其中dis( ...
- HDU 5266 pog loves szh III (线段树+在线LCA转RMQ)
题目地址:HDU 5266 这题用转RMQ求LCA的方法来做的很easy,仅仅须要找到l-r区间内的dfs序最大的和最小的就能够.那么用线段树或者RMQ维护一下区间最值就能够了.然后就是找dfs序最大 ...
- LCA(包含RMQ)
今天看了RMQ问题 ST的实质是动归 于是我来回顾一下LCA(的各种写法) 因为每次考试发现自己连LCA都写不好 费时 First of all, RMQ板子: [一维] #include<bi ...
随机推荐
- 初次接触pyqt
基本了解了pyqt的原理,到http://www.riverbankcomputing.co.uk/news下载安装好qt后,桌面上会出现Qt Designer. 我们可以利用它进行界面的设计,然后保 ...
- 在虚拟中开启Windows 8.1的Hyper-V平台
VM安装windows8开启Hype-V 今天老魏用VM安装了Windows8.1系统,想用此系统来开发一下Windows Phone8,但是要求确实要开启Hyper-V平台技术,本来是没有任何的问题 ...
- Java内存区域与内存溢出异常(二)
了解Java虚拟机的运行时数据区之后,大致知道了虚拟机内存的概况,内存中都放了些什么,接下来将了解内存中数据的其他细节,如何创建.如何布局.如何访问.这里虚拟机以HotSpot为例,内存区域以Java ...
- linux whereis which
whereis 命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s). [root@localhost ~]# whereis svn svn ...
- linux内核分析之进程地址空间管理
1.struct task_struct 进程内核栈是操作系统为管理每一个进程而分配的一个4k或者8k内存大小的一片内存区域,里面存放了一个进程的所有信息,它能够完整的描述一个正在执行的程序:它打开的 ...
- C#结合js 上传文件和删除文件(技术点有:asp.net mvc ,nhibernate,ajax等)
之前做项目的时候要用到上传文件的功能,现在我总结一下,上传文件和删除文件的代码,在以后的使用的过程中也更方便查找. [HttpPost] public ActionResult EditUser() ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- 过长文字自动换行的技巧 Word-Break Word-Wrap
在很多时候,为了防止内容过长把表格或容器撑破, 我们都需要为容器加上自动换行的功能. 实现自动换行,用CSS来实现,通常有两种方式: word-break: 取值为 normal, break-all ...
- AnimateWindow 阻塞当前线程问题
今天查了蛮多的,虽然不是系统的学习,收获也不小.下面说一下我的解决方法: 问题一:采用 AnimateWindow API实现消息窗体FormMsg的动画出现,但由于该方法会阻塞当前线程,造成其他窗体 ...
- js实现网页图片延时加载的原理和代码 提高网站打开速度
有时我们看到一些大型网站,页面如果有很多图片的时候,当你滚动到相应的行时,当前行的图片才即时加载的,这样子的话页面在打开只加可视区域的图片,而其它隐藏的图片则不加载,一定程序上加快了页面加载的速度,对 ...