RMQ和LCA
【内容简介】
讨论解决RMQ、LCA问题的算法、以及RMQ、LCA两类问题的相互转换。它们在字符串处理和生物学计算中有着广泛应用,在信息学奥赛中更是被广泛应用和扩展,所以熟练掌握RMQ和LCA问题就显得十分重要。
※在内容开始,我们假设一个算法预处理时间为 f(n),查询时间为g(n)。这个算法复杂度的标记为<f(n),g(n)>(或f(n)-g(n))。
【关键词】RMQ、LCA、ST算法、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;