区间最值问题:压位分块稀疏表

区间最值问题(RMQ)也就是给定一个序列 $a[n]$, 多次询问 $\min a[l:r]$(最大值同理)。

稀疏表

时间复杂度 $O(n\log n)-O(1)$

空间复杂度 $O(n\log n)$

编程难度 低

设 $f(i, j)=\min a[j:2^i+j]$, 递推预处理。

$$f(i, j)=\begin{cases}a_j,&i=0;\\\min\{f(i-1, j), f(i-1, j+2^{i-1})\},&1 \le i \le \log_2n.\end{cases}$$

在最小值查询中,一个数被统计多次是没关系的。因此设 $t=\left\lfloor\log_2(r-l)\right\rfloor$, $\min a[l:r]=\min\{f(t, l), f(t, r-2^t)\}$.

这里特别说一下,保存 $f(i, j)$ 的数组第一维用 $i$, 第二维用 $j$, 可以使递推时访存连续,减小时间常数。

RMQ标准算法

时间复杂度 $O(n)-O(1)$

空间复杂度 $O(n)$

编程难度 较高

时空复杂度已达理论下界。可惜常数太大,在数据规模不大的时候有时表现还不如稀疏表。

大致思路是,对序列建笛卡尔树,RMQ转成LCA; 对笛卡尔树求欧拉序,LCA转成相邻两项差值为 $\pm 1$ 的约束RMQ问题。

对于约束RMQ问题,每 $\left\lfloor\frac{\log_2 n}{2}\right\rfloor$ 个分一块,整块询问用稀疏表,块内询问由于只有 $O(\sqrt n)$ 种不相似的块,对于每种都可以递推预处理块内任一区间的 RMQ.

压位分块稀疏表

时间复杂度 $O(n(1+\log_{\mathrm w}n))-O(1)$

空间复杂度 $O(n(1+\log_{\mathrm w}n))$

编程难度 较低

这东西是我校身经百战,见多识广的Lagoon队长告诉我的。我也不知道怎么称呼,就自作主张,把它叫做“压位分块稀疏表”吧。

假设机器字长为 $\newcommand{w}{\mathrm w}\w$, 通常 $\w$ 取值为 $32$ 或 $64$.

考虑一个暴力,询问按右端点排序,维护所有左端点对应区间的最小值,这是个单调栈就能解决的东西。查询的时候,最值点就是单调栈上不小于左端点的最左侧点。

如果询问区间长度不超过 $\w$, 那么只需要维护最近 $\w$ 位是否属于单调栈。形式化地,我们维护一个 $\w$ 比特非负整数 $S_r$, $r-i$ 在单调栈上,当且仅当 $S_r$ 的低第 $i$ 位为 $1$.

计算 $S_r$ 只需要在 $S_{r-1} \mathop{\mathrm{lsh}} 1$ 的基础上,一边维护单调栈一边加删一些位。因为 $S_r$ 只占一个字长,随便保存,于是询问也根本不需要按右端点排序了。

查询 $\min a[l:r]$, 也就是求 $S_r$ 的低前 $r-l$ 位中,最高位 $1$ 的位置。通过位运算过滤掉太高的位上的 $1$, 然后调用 __builtin_clz(ll) (这是直接调CPU指令的)来查询最高位。

给序列每 $\mathrm w$ 个分一块,整块的部分用稀疏表查询,块内用上面这个压位暴力。

模板题:洛谷3865【模板】ST表(区间最大值)代码链接

猜你喜欢

转载自www.cnblogs.com/nealchen/p/12285667.html