ST表

JiCanDuck發表於2024-08-30

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;
}

相關文章