Skip to content

wavelet_matrix.hpp

SECTIONData Structure INCLUDEnoya/ds/wavelet_matrix.hpp

静态半开区间第 \(k\) 小、精确频次、值域计数/求和及前驱后继;排序下标并复用缓冲构建,权重使用各输入到 Sum 的转换。

Complexity: Time: \(O(n \log (n + 1))\) build, \(O(\log (\sigma + 2))\) query. Space: \(O((n + 1) \log (\sigma + 2))\), with \(O(1)\) value/Sum operations.

AC 记录:range_kth_smallest, static_range_sum_with_upper_bound。

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include。

/// @complexity Time: O(n log(n+1)) build, O(log(sigma+2)) query.
/// Space: O((n+1) log(sigma+2)), with O(1) value/Sum operations.

#include <algorithm>
#include <bit>
#include <cassert>
#include <limits>
#include <numeric>
#include <optional>
#include <utility>
#include <vector>

namespace noya {

/// @brief Coordinate-compressed static wavelet matrix with range order
/// statistics and value-filtered sums. Values use strict-weak-order equivalence;
/// queries return a representative of each class, while sums use each input's
/// own Sum conversion. Sum is an additive commutative group; arithmetic must fit.
/// The input size fits int. Construction owns its values.
template <class T, class Sum = long long> struct wavelet_matrix {
  int n = 0;
  int lg = 0;
  std::vector<T> xs;
  std::vector<int> mid;
  std::vector<std::vector<int>> s1;
  std::vector<std::vector<Sum>> s0;
  std::vector<Sum> s;

  wavelet_matrix() = default;
  explicit wavelet_matrix(const std::vector<T> &a) { build(a); }

  /// @brief Rebuild the static matrix from values.
  void build(const std::vector<T> &a) {
    assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
    n = int(a.size());
    std::vector<T> values;
    values.reserve(n);
    std::vector<int> rk(n), end{0};
    end.reserve(a.size() + 1);
    {
      std::vector<int> order(n);
      std::iota(order.begin(), order.end(), 0);
      std::stable_sort(order.begin(), order.end(),
                       [&](int i, int j) { return a[i] < a[j]; });
      for (int i = 0; i < n;) {
        int j = i + 1;
        while (j < n && !(a[order[i]] < a[order[j]]))
          ++j;
        int rank = int(values.size());
        values.push_back(a[order[i]]);
        for (int k = i; k < j; ++k)
          rk[order[k]] = rank;
        end.push_back(j);
        i = j;
      }
    }
    int m = int(values.size());
    lg = std::max(1, int(std::bit_width(unsigned(std::max(0, m - 1)))));
    mid.resize(lg);
    s1.resize(lg);
    s0.resize(lg);
    s.resize(a.size() + 1);
    s[0] = Sum{};
    std::vector<int> next_rank(n);
    std::vector<Sum> weight(n), next_weight(n);
    for (int i = 0; i < n; ++i) {
      weight[i] = Sum(a[i]);
      s[i + 1] = s[i] + weight[i];
    }
    // Finish reading a before replacing xs, including for build(xs).
    xs = std::move(values);

    for (int dep = 0; dep < lg; ++dep) {
      int bit = lg - 1 - dep, zeros = 0;
      std::size_t width = std::size_t(1) << bit;
      // Rank-class sizes determine the zero partition before scanning a row.
      for (std::size_t i = 0; i < std::size_t(m); i += 2 * width)
        zeros += end[std::min(i + width, std::size_t(m))] - end[i];
      mid[dep] = zeros;
      auto &ones = s1[dep];
      auto &sums = s0[dep];
      ones.resize(std::size_t(n) + 1);
      sums.resize(std::size_t(n) + 1);
      ones[0] = 0;
      sums[0] = Sum{};
      int at[2] = {0, zeros};
      for (int i = 0; i < n; ++i) {
        int one = (rk[i] >> bit) & 1;
        ones[i + 1] = ones[i] + one;
        sums[i + 1] = sums[i] + (one ? Sum{} : weight[i]);
        int j = at[one]++;
        next_rank[j] = rk[i];
        next_weight[j] = weight[i];
      }
      rk.swap(next_rank);
      weight.swap(next_weight);
    }
  }

  /// @brief Return the number of stored values.
  int size() const { return n; }

  /// @brief Return the k-th smallest value in values[l, r), where k is
  /// zero-indexed.
  T kth_smallest(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    unsigned rk = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int c0 = zr - zl;
      if (k < c0) {
        l = zl;
        r = zr;
      } else {
        k -= c0;
        rk |= 1U << (lg - 1 - dep);
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      }
    }
    assert(rk < xs.size());
    return xs[rk];
  }

  /// @brief Count values x at positions [l, r) satisfying x < hi.
  int count_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    return count_less_rank(l, r, rk);
  }

  /// @brief Count values x at positions [l, r) satisfying lo <= x < hi.
  int range_freq(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return count_less(l, r, hi) - count_less(l, r, lo);
  }

  /// @brief Count occurrences of val at positions [l, r).
  int count(int l, int r, const T &val) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rank = int(std::lower_bound(xs.begin(), xs.end(), val) - xs.begin());
    if (rank == int(xs.size()) || val < xs[rank])
      return 0;
    for (int dep = 0; dep < lg && l < r; ++dep) {
      int ol = s1[dep][l], or_ = s1[dep][r];
      if ((rank >> (lg - 1 - dep)) & 1) {
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l -= ol;
        r -= or_;
      }
    }
    return r - l;
  }

  /// @brief Sum values x at positions [l, r) satisfying lo <= x < hi.
  Sum range_sum(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return sum_less(l, r, hi) - sum_less(l, r, lo);
  }

  /// @brief Return the largest value below hi at positions [l, r), or nullopt.
  std::optional<T> prev_value(int l, int r, const T &hi) const {
    int cnt = count_less(l, r, hi);
    if (cnt == 0) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt - 1);
  }

  /// @brief Return the smallest value at least lo at positions [l, r), or
  /// nullopt.
  std::optional<T> next_value(int l, int r, const T &lo) const {
    int cnt = count_less(l, r, lo);
    if (cnt == r - l) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt);
  }

private:
  void check_range(int l, int r) const { assert(0 <= l && l <= r && r <= n); }

  int count_less_rank(int l, int r, int rk) const {
    check_range(l, r);
    if (rk <= 0) {
      return 0;
    }
    if (rk >= int(xs.size())) {
      return r - l;
    }
    int res = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += zr - zl;
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }

  Sum sum_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return Sum{};
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    if (rk <= 0) {
      return Sum{};
    }
    if (rk >= int(xs.size())) {
      return s[r] - s[l];
    }
    Sum res{};
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += s0[dep][r] - s0[dep][l];
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }
};

} // namespace noya

 // NOYA_WAVELET_MATRIX_HPP
#ifndef NOYA_WAVELET_MATRIX_HPP
#define NOYA_WAVELET_MATRIX_HPP 1

/// @complexity Time: O(n log(n+1)) build, O(log(sigma+2)) query.
/// Space: O((n+1) log(sigma+2)), with O(1) value/Sum operations.

#include <algorithm>
#include <bit>
#include <cassert>
#include <limits>
#include <numeric>
#include <optional>
#include <utility>
#include <vector>

namespace noya {

/// @brief Coordinate-compressed static wavelet matrix with range order
/// statistics and value-filtered sums. Values use strict-weak-order equivalence;
/// queries return a representative of each class, while sums use each input's
/// own Sum conversion. Sum is an additive commutative group; arithmetic must fit.
/// The input size fits int. Construction owns its values.
template <class T, class Sum = long long> struct wavelet_matrix {
  int n = 0;
  int lg = 0;
  std::vector<T> xs;
  std::vector<int> mid;
  std::vector<std::vector<int>> s1;
  std::vector<std::vector<Sum>> s0;
  std::vector<Sum> s;

  wavelet_matrix() = default;
  explicit wavelet_matrix(const std::vector<T> &a) { build(a); }

  /// @brief Rebuild the static matrix from values.
  void build(const std::vector<T> &a) {
    assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
    n = int(a.size());
    std::vector<T> values;
    values.reserve(n);
    std::vector<int> rk(n), end{0};
    end.reserve(a.size() + 1);
    {
      std::vector<int> order(n);
      std::iota(order.begin(), order.end(), 0);
      std::stable_sort(order.begin(), order.end(),
                       [&](int i, int j) { return a[i] < a[j]; });
      for (int i = 0; i < n;) {
        int j = i + 1;
        while (j < n && !(a[order[i]] < a[order[j]]))
          ++j;
        int rank = int(values.size());
        values.push_back(a[order[i]]);
        for (int k = i; k < j; ++k)
          rk[order[k]] = rank;
        end.push_back(j);
        i = j;
      }
    }
    int m = int(values.size());
    lg = std::max(1, int(std::bit_width(unsigned(std::max(0, m - 1)))));
    mid.resize(lg);
    s1.resize(lg);
    s0.resize(lg);
    s.resize(a.size() + 1);
    s[0] = Sum{};
    std::vector<int> next_rank(n);
    std::vector<Sum> weight(n), next_weight(n);
    for (int i = 0; i < n; ++i) {
      weight[i] = Sum(a[i]);
      s[i + 1] = s[i] + weight[i];
    }
    // Finish reading a before replacing xs, including for build(xs).
    xs = std::move(values);

    for (int dep = 0; dep < lg; ++dep) {
      int bit = lg - 1 - dep, zeros = 0;
      std::size_t width = std::size_t(1) << bit;
      // Rank-class sizes determine the zero partition before scanning a row.
      for (std::size_t i = 0; i < std::size_t(m); i += 2 * width)
        zeros += end[std::min(i + width, std::size_t(m))] - end[i];
      mid[dep] = zeros;
      auto &ones = s1[dep];
      auto &sums = s0[dep];
      ones.resize(std::size_t(n) + 1);
      sums.resize(std::size_t(n) + 1);
      ones[0] = 0;
      sums[0] = Sum{};
      int at[2] = {0, zeros};
      for (int i = 0; i < n; ++i) {
        int one = (rk[i] >> bit) & 1;
        ones[i + 1] = ones[i] + one;
        sums[i + 1] = sums[i] + (one ? Sum{} : weight[i]);
        int j = at[one]++;
        next_rank[j] = rk[i];
        next_weight[j] = weight[i];
      }
      rk.swap(next_rank);
      weight.swap(next_weight);
    }
  }

  /// @brief Return the number of stored values.
  int size() const { return n; }

  /// @brief Return the k-th smallest value in values[l, r), where k is
  /// zero-indexed.
  T kth_smallest(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    unsigned rk = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int c0 = zr - zl;
      if (k < c0) {
        l = zl;
        r = zr;
      } else {
        k -= c0;
        rk |= 1U << (lg - 1 - dep);
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      }
    }
    assert(rk < xs.size());
    return xs[rk];
  }

  /// @brief Count values x at positions [l, r) satisfying x < hi.
  int count_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    return count_less_rank(l, r, rk);
  }

  /// @brief Count values x at positions [l, r) satisfying lo <= x < hi.
  int range_freq(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return count_less(l, r, hi) - count_less(l, r, lo);
  }

  /// @brief Count occurrences of val at positions [l, r).
  int count(int l, int r, const T &val) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rank = int(std::lower_bound(xs.begin(), xs.end(), val) - xs.begin());
    if (rank == int(xs.size()) || val < xs[rank])
      return 0;
    for (int dep = 0; dep < lg && l < r; ++dep) {
      int ol = s1[dep][l], or_ = s1[dep][r];
      if ((rank >> (lg - 1 - dep)) & 1) {
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l -= ol;
        r -= or_;
      }
    }
    return r - l;
  }

  /// @brief Sum values x at positions [l, r) satisfying lo <= x < hi.
  Sum range_sum(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return sum_less(l, r, hi) - sum_less(l, r, lo);
  }

  /// @brief Return the largest value below hi at positions [l, r), or nullopt.
  std::optional<T> prev_value(int l, int r, const T &hi) const {
    int cnt = count_less(l, r, hi);
    if (cnt == 0) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt - 1);
  }

  /// @brief Return the smallest value at least lo at positions [l, r), or
  /// nullopt.
  std::optional<T> next_value(int l, int r, const T &lo) const {
    int cnt = count_less(l, r, lo);
    if (cnt == r - l) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt);
  }

private:
  void check_range(int l, int r) const { assert(0 <= l && l <= r && r <= n); }

  int count_less_rank(int l, int r, int rk) const {
    check_range(l, r);
    if (rk <= 0) {
      return 0;
    }
    if (rk >= int(xs.size())) {
      return r - l;
    }
    int res = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += zr - zl;
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }

  Sum sum_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return Sum{};
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    if (rk <= 0) {
      return Sum{};
    }
    if (rk >= int(xs.size())) {
      return s[r] - s[l];
    }
    Sum res{};
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += s0[dep][r] - s0[dep][l];
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }
};

} // namespace noya

#endif // NOYA_WAVELET_MATRIX_HPP
/// @complexity Time: O(n log(n+1)) build, O(log(sigma+2)) query.
/// Space: O((n+1) log(sigma+2)), with O(1) value/Sum operations.

#include <algorithm>
#include <bit>
#include <cassert>
#include <limits>
#include <numeric>
#include <optional>
#include <utility>
#include <vector>

namespace noya {

/// @brief Coordinate-compressed static wavelet matrix with range order
/// statistics and value-filtered sums. Values use strict-weak-order equivalence;
/// queries return a representative of each class, while sums use each input's
/// own Sum conversion. Sum is an additive commutative group; arithmetic must fit.
/// The input size fits int. Construction owns its values.
template <class T, class Sum = long long> struct wavelet_matrix {
  int n = 0;
  int lg = 0;
  std::vector<T> xs;
  std::vector<int> mid;
  std::vector<std::vector<int>> s1;
  std::vector<std::vector<Sum>> s0;
  std::vector<Sum> s;

  wavelet_matrix() = default;
  explicit wavelet_matrix(const std::vector<T> &a) { build(a); }

  /// @brief Rebuild the static matrix from values.
  void build(const std::vector<T> &a) {
    assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
    n = int(a.size());
    std::vector<T> values;
    values.reserve(n);
    std::vector<int> rk(n), end{0};
    end.reserve(a.size() + 1);
    {
      std::vector<int> order(n);
      std::iota(order.begin(), order.end(), 0);
      std::stable_sort(order.begin(), order.end(),
                       [&](int i, int j) { return a[i] < a[j]; });
      for (int i = 0; i < n;) {
        int j = i + 1;
        while (j < n && !(a[order[i]] < a[order[j]]))
          ++j;
        int rank = int(values.size());
        values.push_back(a[order[i]]);
        for (int k = i; k < j; ++k)
          rk[order[k]] = rank;
        end.push_back(j);
        i = j;
      }
    }
    int m = int(values.size());
    lg = std::max(1, int(std::bit_width(unsigned(std::max(0, m - 1)))));
    mid.resize(lg);
    s1.resize(lg);
    s0.resize(lg);
    s.resize(a.size() + 1);
    s[0] = Sum{};
    std::vector<int> next_rank(n);
    std::vector<Sum> weight(n), next_weight(n);
    for (int i = 0; i < n; ++i) {
      weight[i] = Sum(a[i]);
      s[i + 1] = s[i] + weight[i];
    }
    // Finish reading a before replacing xs, including for build(xs).
    xs = std::move(values);

    for (int dep = 0; dep < lg; ++dep) {
      int bit = lg - 1 - dep, zeros = 0;
      std::size_t width = std::size_t(1) << bit;
      // Rank-class sizes determine the zero partition before scanning a row.
      for (std::size_t i = 0; i < std::size_t(m); i += 2 * width)
        zeros += end[std::min(i + width, std::size_t(m))] - end[i];
      mid[dep] = zeros;
      auto &ones = s1[dep];
      auto &sums = s0[dep];
      ones.resize(std::size_t(n) + 1);
      sums.resize(std::size_t(n) + 1);
      ones[0] = 0;
      sums[0] = Sum{};
      int at[2] = {0, zeros};
      for (int i = 0; i < n; ++i) {
        int one = (rk[i] >> bit) & 1;
        ones[i + 1] = ones[i] + one;
        sums[i + 1] = sums[i] + (one ? Sum{} : weight[i]);
        int j = at[one]++;
        next_rank[j] = rk[i];
        next_weight[j] = weight[i];
      }
      rk.swap(next_rank);
      weight.swap(next_weight);
    }
  }

  /// @brief Return the number of stored values.
  int size() const { return n; }

  /// @brief Return the k-th smallest value in values[l, r), where k is
  /// zero-indexed.
  T kth_smallest(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    unsigned rk = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int c0 = zr - zl;
      if (k < c0) {
        l = zl;
        r = zr;
      } else {
        k -= c0;
        rk |= 1U << (lg - 1 - dep);
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      }
    }
    assert(rk < xs.size());
    return xs[rk];
  }

  /// @brief Count values x at positions [l, r) satisfying x < hi.
  int count_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    return count_less_rank(l, r, rk);
  }

  /// @brief Count values x at positions [l, r) satisfying lo <= x < hi.
  int range_freq(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return count_less(l, r, hi) - count_less(l, r, lo);
  }

  /// @brief Count occurrences of val at positions [l, r).
  int count(int l, int r, const T &val) const {
    check_range(l, r);
    if (l == r)
      return 0;
    int rank = int(std::lower_bound(xs.begin(), xs.end(), val) - xs.begin());
    if (rank == int(xs.size()) || val < xs[rank])
      return 0;
    for (int dep = 0; dep < lg && l < r; ++dep) {
      int ol = s1[dep][l], or_ = s1[dep][r];
      if ((rank >> (lg - 1 - dep)) & 1) {
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l -= ol;
        r -= or_;
      }
    }
    return r - l;
  }

  /// @brief Sum values x at positions [l, r) satisfying lo <= x < hi.
  Sum range_sum(int l, int r, const T &lo, const T &hi) const {
    assert(!(hi < lo));
    return sum_less(l, r, hi) - sum_less(l, r, lo);
  }

  /// @brief Return the largest value below hi at positions [l, r), or nullopt.
  std::optional<T> prev_value(int l, int r, const T &hi) const {
    int cnt = count_less(l, r, hi);
    if (cnt == 0) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt - 1);
  }

  /// @brief Return the smallest value at least lo at positions [l, r), or
  /// nullopt.
  std::optional<T> next_value(int l, int r, const T &lo) const {
    int cnt = count_less(l, r, lo);
    if (cnt == r - l) {
      return std::nullopt;
    }
    return kth_smallest(l, r, cnt);
  }

private:
  void check_range(int l, int r) const { assert(0 <= l && l <= r && r <= n); }

  int count_less_rank(int l, int r, int rk) const {
    check_range(l, r);
    if (rk <= 0) {
      return 0;
    }
    if (rk >= int(xs.size())) {
      return r - l;
    }
    int res = 0;
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += zr - zl;
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }

  Sum sum_less(int l, int r, const T &hi) const {
    check_range(l, r);
    if (l == r)
      return Sum{};
    int rk = int(std::lower_bound(xs.begin(), xs.end(), hi) - xs.begin());
    if (rk <= 0) {
      return Sum{};
    }
    if (rk >= int(xs.size())) {
      return s[r] - s[l];
    }
    Sum res{};
    for (int dep = 0; dep < lg; dep++) {
      int ol = s1[dep][l];
      int or_ = s1[dep][r];
      int zl = l - ol;
      int zr = r - or_;
      int bit = lg - 1 - dep;
      if ((rk >> bit) & 1) {
        res += s0[dep][r] - s0[dep][l];
        l = mid[dep] + ol;
        r = mid[dep] + or_;
      } else {
        l = zl;
        r = zr;
      }
    }
    return res;
  }
};

} // namespace noya

 // NOYA_WAVELET_MATRIX_HPP