ホーム > libalgo

Sparce Table

概要

長さ $n$ の配列 $a$ が与えられたとき,$w = 1, 2, 4, 8, \ldots, \lfloor \log_2 n \rfloor$ に対し $\min_{i \in [0,w)} a[i], \min_{i \in [1, w+1)} a[i], \ldots, \min_{i \in [n-w, n)} a[i]$ を事前に計算しておく. すると,区間 $[l, r)$ が与えられたときに区間の最小値を $O(1)$ で計算できる.

使い方

sparce_table<T> st(begin, end)
半開区間 [begin, end) を管理する sparce table を作る.
st.rmq(x, l, r)
x[l, r) の最小値の インデックス を返す.

計算量

構築 : 軽めの $O(n \log n)$, クエリ $O(1)$

実装

template <typename T>
struct sparce_table {
    using size_t = std::size_t;
    template<typename U> using vector = std::vector<U>;

    size_t sz;
    vector<vector<size_t>> min;
    vector<size_t> log2;

    sparce_table() { }

    template <typename iterator>
    sparce_table(iterator first, iterator last)
        : sz(distance(first, last)), min(1, vector<size_t>(sz)) {
        initialize(first);
    }

    template <typename iterator>
    void initialize(iterator dat) {
        log2.resize(sz + 2);
        log2[1] = 0;
        for (size_t i = 2; i < sz + 2; ++i) {
            log2[i] = log2[i / 2] + 1;
        }
        if (sz) min.reserve(log2[(sz + 1) / 2] + 2);
        std::iota(min[0].begin(), min[0].end(), 0);
        for (size_t i = 1; i < 30; ++i) {
            size_t w = 1 << i;
            if (sz + 1 < w) break;
            vector<size_t> next(sz + 1 - w);
            for (size_t l = 0; l < sz + 1 - w; ++l) {
                size_t left = min[i - 1][l], right = min[i - 1][l + w / 2];
                next[l] = dat[left] < dat[right] ? left : right;
            }
            min.emplace_back(move(next));
        }
    }

    template <typename Container>
    size_t rmq(Container const &x, size_t l, size_t r) const {
        size_t i = log2[r - l];
        size_t rr = r - (1 << i);
        l = min[i][l], r = min[i][rr];
        return x[l] < x[r] ? l : r;
    }
};