RMQ和LCA在线算法

原文
学习一下LCA的在线算法。

RMQ

RMQ是查询区间最值的一种方法,其思想非常简单。举例来说,我们想查询区间 [5,37] 中的最小值,如果我们事先知道区间 [5,5+24) 中的最小值以及区间 [3724+1,37+1) 中的最小值,那么我们很容易得到答案。于是问题就变为,我们如何知道区间 [i,i+2k) 中的最小值。显然

Min([i,i+2k))=min(Min([i,i+2(k1)),Min([i+2k1,i+2k))

这样,我们就能利用DP达到目的,上式便是状态转移方程。

dp(i,j) 表示区间 [i,i+2j) 中的最小值,根据状态转移方程有: dp(i,j)=min(dp(i,j1),dp(i+(1<<j1),j1)

于是,初始化dp的代码如下

void RMQININT(int n){
    for(int i = 0 ;i<=n ;i++){
        dp[i][0] = i ;
    }
    for(int j = 1 ;1<<j<=n ;j++){
        //这里减一的原因是右区间为开区间
        for(int i = 0 ;i+(1<<j)-1<=n;i++){
            int x = dp[i][j-1];
            int y = dp[i+(1<<(j-1))][j-1] ;
            dp[i][j] = min(x,y);
        }
    }

}

查询区间 [L,R] 中的最小值也非常简单

ans=min(Min([L,L+2k),Min([R2k+1,R+1))

其中 k 为使得L+2k+1<=R+1满足的最大值。查询的代码如下

int RMQ(int L ,int R){
    int k ;
    while(L+(1<<(k+1)) <= R+1)k++;
    int x = dp[L][k];
    int y = dp[R-(1<<k)+1][k] ;
    return min(x,y) ;
}

LCA在线算法

LCA的概念想必不用多说,求LCA的算法分为在线和离线两种,在线算法就是对于每一次算法求一次LCA,离线算法就是将所有查询离线保存下来,进行批量查询。本文直介绍在线的ST算法。

ST算法是个非常容易理解的算法,实现也非常简单。举例说明:

假设现在有这么一棵树:

1

我想查询节点4和节点10的最近公共祖先。

首先,我们从节点1开始,深度优先搜索这棵树,深搜的顺序为:

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

这个序列貌似可以称之为欧拉序列,在深搜的同时,我们保存一下每个节点第一次出现在欧拉序列中的位置first[],以及欧拉序列每一个位置对应的节点在树中的深度,这些信息在后面会用到。

求4和10的最近公共祖先的步骤也比较简单。通过深搜,我们知道,4第一次出现在欧拉序列中的位置为3;10第一次出现在欧拉序列中的位置为16。那么4和10的最近公共祖先就是欧拉序列第3位到第16位的节点中,深度最小的那一个。求深度最小的节点,可以采用RMQ。

那么为什么这么做就能够得到LCA呢,其实道理很简单,dfs得到的欧拉序列中,任意一段都是整棵树的一棵子树,并且这棵子树必定是包含待查询的两点的最小子树(这里说的最小,不考虑不包含待查询点的分叉),这也就说明,该子树中深度最小的点必定为待查询两点的最近公共祖先。

代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值