ST表可以在靜態空間中 \(O(log)\) 查詢最值,但需要 \(O(nlogn)\)初始化。
字首和皆知盡人需要可逆性,\(+\) 的逆運算 為 \(-\), $\times $ 的逆運算為 \({\div}\)(非0), ^的逆運算為 ^本身, 但\(max\) \(min\),不具有逆運算。
所以ST表粉墨登場(
SL表利用的是倍增思想。任何一個數均可以寫成 \(a_1 \times 2^0 + a_2 \times 2^1 + \dots + a_k \times 2^{k - 1}\),就可以把最值用其拼接。
求取答案的部分相對簡單,分解後在求值。也有簡單辦法,因為最值重合不影響,即可使用 cmath
中的 \(log2(x)\) 找到 \(\le x\) 的最大2的冪。
現在問題來到如何預處理。定義 \(f_{ji}\),表示跨度和起點,當然跨度是 \(2^j\)。每次轉移就是之前兩端拼起來$ f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);$
#include <cmath>
#include <iostream>
using namespace std;
const int kMaxN = 1e5 + 5;
int n, m, a[kMaxN], f[kMaxN][30];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
f[i][0] = a[i];
}
for (int i = 1; (1 << i) <= n; i++) { // 跨度
for (int j = 1; j + (1 << i) - 1 <= n; j++) { // 起點
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
for (int i = 1, l, r; i <= m; i++) {
cin >> l >> r;
int u = log2(r - l + 1);
cout << max(f[l][u], f[r - (1 << u) + 1][u]) << '\n';;
}
return 0;
}