スパーステーブル (Sparse Table)
lib/data_structure/sparse_table.hpp
概要
更新のない列に対する冪等な区間演算を、$O(n\log n)$ の前計算後に $O(1)$ で求める。 Range Minimum Query、Range Maximum Query、GCD などに適する。
インクルード
#include "data_structure/sparse_table.hpp"
クラス
| 名前 | 説明 |
|---|---|
sparse_table |
スパーステーブル |
例
#include <iostream>
#include <vector>
#include "data_structure/sparse_table.hpp"
int main() {
std::vector<int> a = {5, 2, 7, 1, 3};
sparse_table<Min<int>> table(a);
std::cout << table.prod(1, 4) << '\n'; // min(2, 7, 1)
}
出力
1
備考
- クエリでは区間を同じ長さの 2 区間で重ねて覆うため、演算が冪等である必要がある。
- 和や積のような非冪等演算には
data_structure/disjoint_sparse_table.hpp、 更新が必要ならsegtree/segment_tree.hppを使う。 - 空区間
prod(l, l)はM::id()を返す。構築後の要素更新はできない。
検証
依存するヘッダ(1)
-
モノイドの要件
lib/segtree/monoid.hpp
このヘッダを使うライブラリ(3)
-
線形 Sparse Table
lib/data_structure/linear_sparse_table.hpp -
auxiliary tree(補助木 / virtual tree)
lib/tree/auxiliary_tree.hpp -
線形前処理・定数時間クエリのLCA
lib/tree/linear_lca.hpp
検証プログラム(5)
-
test/aoj/challenge/2270.test.cpp
test/aoj/challenge/2270.test.cpp -
test/aoj/course/ALDS1_14_D.test.cpp
test/aoj/course/ALDS1_14_D.test.cpp -
test/yosupo/data_structure/static_rmq.test.cpp
test/yosupo/data_structure/static_rmq.test.cpp -
test/yosupo/tree/lowest_common_ancestor.test.cpp
test/yosupo/tree/lowest_common_ancestor.test.cpp -
test/yukicoder/0901.test.cpp
test/yukicoder/0901.test.cpp
実装
#pragma once
#include <bit>
#include <cassert>
#include <vector>
#include "segtree/monoid.hpp"
/// @file
/// @brief スパーステーブル (Sparse Table)
/// @details 更新のない列に対する冪等な区間演算を、$O(n\log n)$ の前計算後に $O(1)$ で求める。
/// Range Minimum Query、Range Maximum Query、GCD などに適する。
/// @note クエリでは区間を同じ長さの 2 区間で重ねて覆うため、演算が冪等である必要がある。
/// @note 和や積のような非冪等演算には `data_structure/disjoint_sparse_table.hpp`、
/// 更新が必要なら `segtree/segment_tree.hpp` を使う。
/// @note 空区間 `prod(l, l)` は `M::id()` を返す。構築後の要素更新はできない。
/// @code
/// #include <iostream>
/// #include <vector>
/// #include "data_structure/sparse_table.hpp"
///
/// int main() {
/// std::vector<int> a = {5, 2, 7, 1, 3};
/// sparse_table<Min<int>> table(a);
/// std::cout << table.prod(1, 4) << '\n'; // min(2, 7, 1)
/// }
/// @endcode
/// @brief スパーステーブル
/// @details 冪等なモノイドを仮定する。
/// @complexity 構築は $O(n\log n)$、区間積は $O(1)$
template <monoid M>
struct sparse_table {
private:
using T = typename M::value_type;
public:
/// @brief 空のtableを構築する
/// @complexity $O(1)$
sparse_table() = default;
/// @brief 列vから構築する
/// @complexity $O(n\log n)$
sparse_table(const std::vector<T> &v) : _size(v.size()), data() {
int b = std::max(1, std::countr_zero(std::bit_ceil<unsigned>(_size)));
data.emplace_back(v);
for (int i = 1; i < b; ++i) data.emplace_back(_size + 1 - (1 << i));
for (int i = 1; i < b; ++i) {
for (int j = 0; j + (1 << i) <= _size; ++j) {
data[i][j] = M::op(data[i - 1][j], data[i - 1][j + (1 << (i - 1))]);
}
}
}
/// @brief 半開区間[l,r)の積を返す
/// @complexity $O(1)$
T prod(int l, int r) const {
assert(0 <= l && l <= r && r <= _size);
if (l == r) return M::id();
if (l + 1 == r) return data[0][l];
int b = 31 - std::countl_zero<unsigned>(r - l - 1);
return M::op(data[b][l], data[b][r - (1 << b)]);
}
private:
int _size;
std::vector<std::vector<T>> data;
};