Unverified Commit 39a64ac7 authored by orzAtalod's avatar orzAtalod Committed by GitHub
Browse files

Apply suggestions from code review



(还有这种方便的操作

Co-Authored-By: default avatarouuan <1609483441@qq.com>
parent 03656a6c
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@ RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最

 [ST 表模板题](https://www.luogu.org/problemnew/show/P3865) 

题目大意:给定 $n$ 个数,有 $m$ 个询问,对于每个询问,你需要回答区间 $[x,y]$ 中的最大值。
题目大意:给定 $n$ 个数,有 $m$ 个询问,对于每个询问,你需要回答区间 $[l,r]$ 中的最大值。

考虑暴力做法。每次都对区间 $[x,y]$ 扫描一遍,求出最大值。
考虑暴力做法。每次都对区间 $[l,r]$ 扫描一遍,求出最大值。

显然,这个算法会超时。

@@ -20,7 +20,7 @@ ST 表基于 [倍增](../basic/binary-acc.md) 思想,可以做到 $\Theta(n\lo

基于倍增思想,我们考虑如何求出区间最大值。可以发现,如果按照一般的倍增流程,每次跳 $2^i$ 步的话,询问时的复杂度仍旧是 $\Theta(\log n)$ ,并没有比线段树更优,反而预处理一步还比线段树慢。

但是,我们发现 $\max(x,x)=x$ ,也就是说,区间最大值是一个具有 `可重复贡献` 性质的问题。用于求区间最大值的时候采用的预处理区间可以重叠而不影响答案。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次,这是我们所不愿意看到的。
但是,我们发现 $\max(x,x)=x$ ,也就是说,区间最大值是一个具有可重复贡献性质的问题,也就是说,即使用来求最大值的预处理区间有重叠部分,只要这些区间的并是所求的区间,最终计算出的答案就是正确的。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次,这是我们所不愿意看到的。

如果手动模拟一下,可以发现我们能使用至多两个预处理过的区间来覆盖询问区间,也就是说,询问时的时间复杂度被降至 $\Theta(1)$ ,在处理有大量询问的题目时十分有效。

@@ -92,7 +92,7 @@ int main() {

1.  输入输出数据一般很多,建议开启输入输出优化。

2.  每次用 [std::log](https://en.cppreference.com/w/cpp/numeric/math/log) 重新计算 log 函数值并不值得,建议如下预处理:
2.  每次用 [std::log](https://en.cppreference.com/w/cpp/numeric/math/log) 重新计算 log 函数值并不值得,建议进行如下预处理:

$$
\left\{\begin{aligned}
@@ -123,7 +123,7 @@ ST 表能较好的维护“可重复贡献”的区间信息(同时也应满

## 附录:ST 表求区间 GCD 的时间复杂度分析

在算法运行的时候,可能要经过 $\Theta(\log n)$ 次迭代。每一次迭代都可能会使用 GCD 函数进行递归,令值域为 $w$ ,GCD 函数的时间复杂度最高是 $\Omega(\log w)$ 的,所以总时间复杂度看似有 $\Omicron(n\log n\log w)$ 。
在算法运行的时候,可能要经过 $\Theta(\log n)$ 次迭代。每一次迭代都可能会使用 GCD 函数进行递归,令值域为 $w$ ,GCD 函数的时间复杂度最高是 $\Omega(\log w)$ 的,所以总时间复杂度看似有 $O(n\log n\log w)$ 。

但是,在 GCD 的过程中,每一次递归(除最后一次递归之外)都会使数列中的某个数至少减半,而数列中的数最多减半的次数为 $\log_2 (w^n)=\Theta(n\log w)$ ,所以,GCD 的递归部分最多只会运行 $O(n\log w)$ 次。再加上循环部分(以及最后一层递归)的 $\Theta(n\log n)$ ,最终时间复杂度则是 $O(n(\log w+\log x))$ ,由于可以构造数据使得时间复杂度为 $\Omega(n(\log w+\log x))$ ,所以最终的时间复杂度即为 $\Theta(n(\log w+\log x))$ 。

@@ -144,4 +144,4 @@ ST 表能较好的维护“可重复贡献”的区间信息(同时也应满

    在一次迭代中,所花费的时间相当于迭代循环所花费的时间与 GCD 所花费的时间之和。其中,GCD 花费的时间有长有短。最短可能只有两次甚至一次递归,而最长可能有 $O(\log w)$ 次递归。但是,GCD 过程中,除最开头一层与最末一层以外,每次递归都会使“待考虑数列”中的某个结果至少减半。即, $\Phi(A)$ 会减少至少 $1$ ,该层递归所用的时间可以被势能函数均摊。

    同时,我们可以看到, $\Phi(A)$ 的初值最大为 $\log_2 (w^n)=\Theta(n\log w)$ ,而 $\Phi(A)$ 不增。所以,ST 表预处理部分的时间复杂度为 $\Omicron(n(\log w+\log n))$ 。
    同时,我们可以看到, $\Phi(A)$ 的初值最大为 $\log_2 (w^n)=\Theta(n\log w)$ ,而 $\Phi(A)$ 不增。所以,ST 表预处理部分的时间复杂度为 $O(n(\log w+\log n))$ 。