セグメントツリー (点変更・区間クエリ)
概要
セグメントツリーとは,区間を $O(\log N)$ 個に分割することで区間に対するクエリを高速に処理するデータ構造です.
RMQ や RSQ などの 様々なバリエーションがありますが,乗せられるデータは全て monoid なので, それらを表現する trait を組み込めるようにすれば一般的な実装ができます. 詳細は下記の koba さんや tomcatowl さんの記事を見てください.
使い方
実装
template <typename monoid>
struct segment_tree {
using M = monoid;
using T = typename M::value_type;
std::size_t sz;
std::vector<T> x;
segment_tree(std::size_t n = 0) {
sz = 1;
while (sz < n) sz *= 2;
x.assign(sz * 2, M::id());
initialize();
}
template <typename iterator>
segment_tree(iterator first, iterator last) {
sz = 1;
std::size_t n = std::distance(first, last);
while (sz < n) sz *= 2;
x.assign(sz * 2, M::id());
std::copy(first, last, x.begin() + sz);
initialize();
}
void fill(const T &val) {
std::fill(x.begin() + sz, x.end(), val);
initialize();
}
void initialize() {
for (int i = (int)sz - 1; i >= 1; --i) {
x[i] = M::op(x[i * 2 + 0], x[i * 2 + 1]);
}
}
T accumulate(std::size_t l, std::size_t r) const {
T al = M::id(), ar = M::id();
for (l += sz, r += sz; l < r; l /= 2, r /= 2) {
if (l & 1) al = M::op(al, x[l++]);
if (r & 1) ar = M::op(x[--r], ar);
}
return M::op(al, ar);
}
void update(std::size_t i, const T &val) {
x[i += sz] = val;
while (i > 1) {
x[i / 2] = M::op(x[i], x[i ^ 1]);
i /= 2;
}
}
T operator[](std::size_t i) const { return x[sz + i]; }
};
template <typename T>
struct min_monoid {
using value_type = T;
static constexpr T id() { return std::numeric_limits<T>::max(); }
static T op(const T &a, const T &b) { return std::min(a, b); }
};
template <typename T>
struct max_monoid {
using value_type = T;
static constexpr value_type id() { return std::numeric_limits<value_type>::min(); }
static value_type op(const value_type &a, const value_type &b) { return std::max(a, b); }
};
template <typename T>
struct sum_monoid {
using value_type = T;
static constexpr value_type id() { return 0; }
static value_type op(const value_type &a, const value_type &b) { return a + b; }
};
template <typename value_type>
using rmq = segment_tree<min_monoid<value_type>>;
template <typename value_type>
using rsq = segment_tree<sum_monoid<value_type>>;
検証
http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=2386727#1
参考文献
- 蟻本
- データ構造と代数構造
- データ構造と代数(前編) データ構造と代数(後編)
- Efficient and easy segment trees
- クエリをビット演算で高速化するテク