在此之前,我写过另一篇博客,是倍增(在线)求LCA。有兴趣的同学可以去看一看。概念以及各种暴力就不在这里说了,那篇博客已经有介绍了。

不会ST算法的同学点这里


ST(RMQ)算法在线求LCA

这个算法的思想,就是将LCA问题转化成RMQ问题。

怎么将LCA转成RMQ?

我们首先用dfsO(N)遍历一遍。比如下图:



得到一个dfs序(从儿子回到父亲也要算一遍):

1->2->4->7->4->8->4->2->5->2->6->9->6->10->6->2->1->3->1

可以简单地理解成这样:你一开始在根节点,一直向下走,发现尽头就倒退,向另一个方向走。最后你还会回到根节点。你遍历这个树的顺序就是一个这样的dfs序。

有没有发现什么规律?

设r[x]表示x在这个dfs序当中第一次出现的位置,deep[x]表示x的深度。

那么可以发现,如果要求x和y的LCA,r[x]~r[y]这一段区间内一定有它们的LCA,而且还是区间中深度最小的那个。

这是为什么?

只要你懂dfs,简单思考一下就能明白。到达x点后,再到y点,必须经过过它们的LCA,因为这是一棵树,两个点之间有且只有一条路径

为什么它在区间中深度最小?

因为dfs的原因,遍历以LCA(x,y)为根的子树时,不遍历完所有以LCA(x,y)为根的点是不会回去的。然而x、y一定在以LCA(x,y)为根的子树当中,所以这也是成立的。

具体怎么做?

首先,用dfsO(n)求出dfs序、r数组和deep数组。

然后,套一个纯的ST(RMQ)。设f[i][j]表示j~j+2^i-1的点当中,deep值最小的是哪个。

预处理做完了,接下来就可以在线O(1)回答询问了。

注意事项

这个dfs序长度是2n-1的,原因:每个点经过的次数=儿子个数+1。那么所有点的儿子个数一共有n-1,因为没有根节点。所有是2n-1的。

在线O(1)回答的时候,有的人求对数使用log(x)/log(2)的形式。实际上没必要,因为C++中有个东西叫log2,直接用就好。


代码实现

例题 P3379【模板】最近公共祖先(LCA)

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n,_n,m,s;//_n是用来放元素进dfs序里,最终_n=2n-1
struct EDGE
{
int to;
EDGE* las;
} e[1000001];//前向星存边
EDGE* last[500001];
int sx[1000001];//顺序,为dfs序
int f[21][1000001];//用于ST算法
int deep[500001];//深度
int r[500001];//第一次出现的位置
void dfs(int,int,int);
int min(int a,int b){return deep[a]<deep[b]?a:b;}
int query(int,int);
int main()
{
scanf("%d%d%d",&n,&m,&s);
int i,j=0,x,y;
for (i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
e[++j]={y,last[x]};
last[x]=e+j;
e[++j]={x,last[y]};
last[y]=e+j;
}
dfs(s,0,0);
//以下是ST算法
for (i=1;i<=_n;++i)
f[0][i]=sx[i];
int ni=int(log2(_n)),nj,tmp;
for (i=1;i<=ni;++i)
{
nj=_n+1-(1<<i);
tmp=1<<i-1;
for (j=1;j<=nj;++j)
f[i][j]=min(f[i-1][j],f[i-1][j+tmp]);
}
//以下是询问,对于每次询问,可以O(1)回答
while (m--)
{
scanf("%d%d",&x,&y);
printf("%d\n",query(r[x],r[y]));
}
}
void dfs(int t,int fa,int de)
{
sx[++_n]=t;
r[t]=_n;
deep[t]=de;
EDGE* ei;
for (ei=last[t];ei;ei=ei->las)
if (ei->to!=fa)
{
dfs(ei->to,t,de+1);
sx[++_n]=t;
}
}
int query(int l,int r)
{
if (l>r)
{
//交换
l^=r;
r^=l;
l^=r;
}
int k=int(log2(r-l+1));
return min(f[k][l],f[k][r-(1<<k)+1]);
}

ST(RMQ)算法(在线)求LCA的更多相关文章

  1. SPOJ 10628 Count on a tree(Tarjan离线 | RMQ-ST在线求LCA+主席树求树上第K小)

    COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to  ...

  2. Tarjan算法离线 求 LCA(最近公共祖先)

    本文是网络资料整理或部分转载或部分原创,参考文章如下: https://www.cnblogs.com/JVxie/p/4854719.html http://blog.csdn.net/ywcpig ...

  3. 求LCA最近公共祖先的离线Tarjan算法_C++

    这个Tarjan算法是求LCA的算法,不是那个强连通图的 它是 离线 算法,时间复杂度是 O(m+n),m 是询问数,n 是节点数 它的优点是比在线算法好写很多 不过有些题目是强制在线的,此类离线算法 ...

  4. 求LCA最近公共祖先的在线ST算法_C++

    ST算法是求最近公共祖先的一种 在线 算法,基于RMQ算法,本代码用双链树存树 预处理的时间复杂度是 O(nlog2n)   查询时间是 O(1) 的 另附上离线算法 Tarjan 的链接: http ...

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

  6. 求LCA最近公共祖先的在线倍增算法模板_C++

    倍增求 LCA 是在线的,而且比 ST 好写多了,理解起来比 ST 和 Tarjan 都容易,于是就自行脑补吧,代码写得容易看懂 关键理解 f[i][j] 表示 i 号节点的第 2j 个父亲,也就是往 ...

  7. 倍增(在线)求LCA

    这几天,提高B组总是有求LCA的题.由于我是蒟蒻,所以老是做不出来,直接上暴力.现在才弄懂. 没耐心看前面部分的大神门可以直接看后面. ST(RMQ)算法(在线)求LCA LCA是什么? 在一棵树上, ...

  8. RMQ算法 (ST算法)

     概述: RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中 ...

  9. BZOJ1906树上的蚂蚁&BZOJ3700发展城市——RMQ求LCA+树链的交

    题目描述 众所周知,Hzwer学长是一名高富帅,他打算投入巨资发展一些小城市. Hzwer打算在城市中开N个宾馆,由于Hzwer非常壕,所以宾馆必须建在空中,但是这样就必须建立宾馆之间的连接通道.机智 ...

随机推荐

  1. 什么是PoE、PSE、PD设备?

    一个完整的PoE系统包括供电端设备(PSE, Power Sourcing Equipment)和受电端设备(PD, Power Device)两部分.PSE设备是为以太网客户端设备供电的设备,同时也 ...

  2. 用mybatis时log4j总是不记录sql语句

    log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).log4j:WARN ...

  3. 查看Linux服务器公网IP

    参考:https://www.cnblogs.com/pyyu/p/8545896.html 方法1:curl ifconfig.me 方法2:curl cip.cc

  4. MySQL安装配置及测试

    1. 安装包下载 点击下载地址:https://dev.mysql.com/downloads/installer/打开页面,滑到较底端,按如下选择下载: 会弹出一个注册登录页面,可以不用管,直接点击 ...

  5. iOS开发系列-NSFileManager

    NSFileManager NSFileManager类主要对文件和目录的操作(删除.修改.移动.复制等等).

  6. sysobjects syscolumns

    在sysobjects系统表中存储着数据库的所有对象,每个对象都有一个唯一的id号进行标识.object_id就是根据对象名称返回该对象的id.反之,object_name是根据对象id返回对象名称. ...

  7. 前端常用的库和实用技术之JavaScript高级函数

    1.惰性载入函数 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. JAVA数据结构之红-黑树

    本篇博客我会重点介绍对红-黑树的理解,重点介绍红-黑树的查找,这里我们将要讨论的算法称为自顶向下插入,也就是把沿着树向下查找插入点 Ⅰ.平衡树和非平衡树 平衡树和非平衡树:当插入一组数据关键字是按照升 ...

  9. CSIC_716_20191113【装饰器进阶以及迭代器】

    装饰器的进阶主要包含叠加装饰器和有参装饰器 叠加装饰器:在一个被装饰的对象中,添加多个装饰器. 为什么要用叠加装饰器的原因:    -每一个新的功能都应该写一个新的装饰器,否则会导致,代码冗余,结构不 ...

  10. Qt Creator配置

    1.安装Git sudo apt install git 2.配置Git 用户和邮箱: git config --global user.name "xxx" git config ...