RMQ和LCA

本文深入探讨了RMQ(Range Minimum/Maximum Query)和LCA(Lowest Common Ancestor)问题的算法,包括ST算法、sqrt(n)算法、Tarjan离线算法等。RMQ问题求给定区间内的最值,LCA问题求树中两个节点的最近公共祖先。文章介绍了如何将RMQ问题转换为LCA问题,以及反之的转换,并提供了不同算法的时间复杂度分析。
摘要由CSDN通过智能技术生成

 RMQ和LCA

【内容简介】

  讨论解决RMQLCA问题的算法、以及RMQLCA两类问题的相互转换。它们在字符串处理和生物学计算中有着广泛应用,在信息学奥赛中更是被广泛应用和扩展,所以熟练掌握RMQLCA问题就显得十分重要。

在内容开始,我们假设一个算法预处理时间为 f(n),查询时间为g(n)。这个算法复杂度的标记为<f(n),g(n)>(f(n)-g(n))

【关键词】RMQLCAST算法、Tarjian离线算法、±1RMQ、笛卡尔树、欧拉序列、并查集

一、RMQ(Range Minimum/Maximum Query)问题:

RMQ问题是求给定区间中的最值问题,如下图所示:


RMQ问题(图中记录的是最小值的位置)

当然,最简单的算法是O(n)的,但是对于查询次数很多m(假设有100万次),则这个算法的时间复杂度为O(mn),显然时间效率太低。可以用线段树将查询算法优化到O(logn)(在线段树中保存线段的最值),而线段树的预处理时间复杂度为O(n),线段树整体复杂度为<O(n), O(logn)>。不过,Sparse_Table算法(简称ST算法)才是最好的:它可以在O(nlogn)的预处理以后实现O(1)的查询效率,即整体时间复杂度为<O(nlogn),O(1)>。

1.1 ST算法(★★★★

ST算法,即Sparse Table算法。下面把ST算法分成预处理和查询两部分来说明(以求最小值为例),它的时间复杂度为<O (nlogn), O(1)>

1.1.1预处理:

预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,即f[i,j]表示从第i个数起连续2^j个数中的最小值。我们可以开辟一个数组专门来保存f(i, j)的值。

例如,f(1, 0)表示[1,1]之间的最小值,就是num[1];f(1, 2)表示[1, 4]之间的最小值, f(2, 4)表示[2, 17]之间的最小值。

注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1),j-1)导出, 而递推的初值(所有的f(i, 0) =num[i])都是已知的。所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。

ST算法(图中记录的是最小值的位置)

ST算法的状态转移方程:

例如:f(2,3)保存的是a[2],a[3],a[4],……,a[9]中的最小值,而f(2,3)=min(f(2,2),f(6,2))=min((a[2],a[3],a[4],a[5]),(a[6],a[7],a[8],a[9]))

1.1.2 查询:

假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k, 使得k满足,于是我们就可以把[m, n]分成两个(部分重叠的)长度为的区间: , ;

而我们之前已经求出了为 的最小值, 为 的最小值。

我们只要返回其中更小的那个, 就是我们想要的答案, 这个算法的时间复杂度是O(1)的。

k= trunc(ln(r-l+1)/ln(2)); // [l,r]之间的最小值 

ans:=max(F[l,k],F[r-2^k+1,k]);

例如, rmq(1,12)= min(f(1,3), f(5,3)) ()


ST算法的O(1)查询(有部分重叠)

RMQ区间最大值:【参考代码】(部分)

functionmax(x,y:longint):longint;

begin

 max:=x;

 ify>xthenmax:=y;

end;

 

functionquery(s,t:longint):longint;  // 查询[s,t]间的最大值

var

 k:longint;

begin

 k:=trunc(ln(t-s+1)/ln(2));

 query:=max(a[s,k],a[t-(1<<k)+1,t]); // max(a[s,k],a[t-2^k+1,t])

end;

 

procedureinit;

var

 i,j,p:longint;

begin

 assign(input,'rmq-st.in');

 reset(input);

 readln(n);

 fori:=1tondoread(a[i,0]);

 p:=trunc(ln(n)/ln(2));

 forj:=1topdo       // a[i,j]表示从i开始,2^j个元素的最大值

 fori:=1to(n-(1<<j)+1)do    //n-(1<<j)+1 n-2^j+1

   a[i,j]:=max(a[i,j-1],a[i+1<<(j-1),j-1]);

 close(input);

end;

1.2 巧妙易实现的分块 -- sqrt(n)算法:(

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值