Skip to content

persistent_order_statistics.hpp

SECTIONData Structure INCLUDEnoya/persistent_order_statistics.hpp

保留数组各前缀的历史版本,并回答区间第 \(k\) 小或值域计数;适合静态区间顺序统计。

Complexity: Time: O(n log sigma) build and O(log sigma) per query. Space: O(n log sigma).

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n log sigma) build and O(log sigma) per query.
/// Space: O(n log sigma).

#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Persistent frequency segment tree for range kth and rank queries;
/// build O(n log n), each query O(log n).
template <class T> struct persistent_order_statistics {
  struct node {
    int ls = 0;
    int rs = 0;
    int cnt = 0;
  };

  int n = 0;
  std::vector<T> xs;
  std::vector<node> tr{{}};
  std::vector<int> rt{0};

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

  void build(const std::vector<T> &a) {
    n = int(a.size());
    xs = a;
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    tr.assign(1, {});
    rt.assign(n + 1, 0);
    for (int idx = 0; idx < n; idx++) {
      int pos =
          int(std::lower_bound(xs.begin(), xs.end(), a[idx]) - xs.begin());
      rt[idx + 1] = insert(rt[idx], 0, int(xs.size()), pos);
    }
  }

  /// @brief Return the zero-indexed kth smallest value in [l, r).
  T kth(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    int low = 0;
    int hi = int(xs.size());
    int rl = rt[l];
    int rr = rt[r];
    while (hi - low > 1) {
      int mid = std::midpoint(low, hi);
      int cl = tr[tr[rr].ls].cnt - tr[tr[rl].ls].cnt;
      if (k < cl) {
        rl = tr[rl].ls;
        rr = tr[rr].ls;
        hi = mid;
      } else {
        k -= cl;
        rl = tr[rl].rs;
        rr = tr[rr].rs;
        low = mid;
      }
    }
    return xs[low];
  }

  /// @brief Count values smaller than bound in [l, r).
  int count_less(int l, int r, const T &lim) const {
    assert(0 <= l && l <= r && r <= n);
    int pos = int(std::lower_bound(xs.begin(), xs.end(), lim) - xs.begin());
    return prefix_count(rt[r], 0, int(xs.size()), pos) -
           prefix_count(rt[l], 0, int(xs.size()), pos);
  }

  /// @brief Count values in [lo, big) within positions [l, r).
  int count(int l, int r, const T &lo, const T &big) const {
    return count_less(l, r, big) - count_less(l, r, lo);
  }

private:
  int insert(int rt0, int low, int hi, int pos) {
    int cur = int(tr.size());
    tr.push_back(tr[rt0]);
    tr[cur].cnt++;
    if (hi - low == 1) {
      return cur;
    }
    int mid = std::midpoint(low, hi);
    if (pos < mid) {
      tr[cur].ls = insert(tr[rt0].ls, low, mid, pos);
    } else {
      tr[cur].rs = insert(tr[rt0].rs, mid, hi, pos);
    }
    return cur;
  }

  int prefix_count(int rt0, int low, int hi, int r) const {
    if (rt0 == 0 || r <= low) {
      return 0;
    }
    if (hi <= r) {
      return tr[rt0].cnt;
    }
    int mid = std::midpoint(low, hi);
    return prefix_count(tr[rt0].ls, low, mid, r) +
           prefix_count(tr[rt0].rs, mid, hi, r);
  }
};

} // namespace noya
#ifndef NOYA_PERSISTENT_ORDER_STATISTICS_HPP
#define NOYA_PERSISTENT_ORDER_STATISTICS_HPP 1

/// @complexity Time: O(n log sigma) build and O(log sigma) per query.
/// Space: O(n log sigma).

#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Persistent frequency segment tree for range kth and rank queries;
/// build O(n log n), each query O(log n).
template <class T> struct persistent_order_statistics {
  struct node {
    int ls = 0;
    int rs = 0;
    int cnt = 0;
  };

  int n = 0;
  std::vector<T> xs;
  std::vector<node> tr{{}};
  std::vector<int> rt{0};

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

  void build(const std::vector<T> &a) {
    n = int(a.size());
    xs = a;
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    tr.assign(1, {});
    rt.assign(n + 1, 0);
    for (int idx = 0; idx < n; idx++) {
      int pos =
          int(std::lower_bound(xs.begin(), xs.end(), a[idx]) - xs.begin());
      rt[idx + 1] = insert(rt[idx], 0, int(xs.size()), pos);
    }
  }

  /// @brief Return the zero-indexed kth smallest value in [l, r).
  T kth(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    int low = 0;
    int hi = int(xs.size());
    int rl = rt[l];
    int rr = rt[r];
    while (hi - low > 1) {
      int mid = std::midpoint(low, hi);
      int cl = tr[tr[rr].ls].cnt - tr[tr[rl].ls].cnt;
      if (k < cl) {
        rl = tr[rl].ls;
        rr = tr[rr].ls;
        hi = mid;
      } else {
        k -= cl;
        rl = tr[rl].rs;
        rr = tr[rr].rs;
        low = mid;
      }
    }
    return xs[low];
  }

  /// @brief Count values smaller than bound in [l, r).
  int count_less(int l, int r, const T &lim) const {
    assert(0 <= l && l <= r && r <= n);
    int pos = int(std::lower_bound(xs.begin(), xs.end(), lim) - xs.begin());
    return prefix_count(rt[r], 0, int(xs.size()), pos) -
           prefix_count(rt[l], 0, int(xs.size()), pos);
  }

  /// @brief Count values in [lo, big) within positions [l, r).
  int count(int l, int r, const T &lo, const T &big) const {
    return count_less(l, r, big) - count_less(l, r, lo);
  }

private:
  int insert(int rt0, int low, int hi, int pos) {
    int cur = int(tr.size());
    tr.push_back(tr[rt0]);
    tr[cur].cnt++;
    if (hi - low == 1) {
      return cur;
    }
    int mid = std::midpoint(low, hi);
    if (pos < mid) {
      tr[cur].ls = insert(tr[rt0].ls, low, mid, pos);
    } else {
      tr[cur].rs = insert(tr[rt0].rs, mid, hi, pos);
    }
    return cur;
  }

  int prefix_count(int rt0, int low, int hi, int r) const {
    if (rt0 == 0 || r <= low) {
      return 0;
    }
    if (hi <= r) {
      return tr[rt0].cnt;
    }
    int mid = std::midpoint(low, hi);
    return prefix_count(tr[rt0].ls, low, mid, r) +
           prefix_count(tr[rt0].rs, mid, hi, r);
  }
};

} // namespace noya

#endif // NOYA_PERSISTENT_ORDER_STATISTICS_HPP
#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

/// @complexity Time: O(n log sigma) build and O(log sigma) per query.
/// Space: O(n log sigma).

namespace noya {

/// @brief Persistent frequency segment tree for range kth and rank queries;
/// build O(n log n), each query O(log n).
template <class T> struct persistent_order_statistics {
  struct node {
    int ls = 0;
    int rs = 0;
    int cnt = 0;
  };

  int n = 0;
  std::vector<T> xs;
  std::vector<node> tr{{}};
  std::vector<int> rt{0};

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

  void build(const std::vector<T> &a) {
    n = int(a.size());
    xs = a;
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    tr.assign(1, {});
    rt.assign(n + 1, 0);
    for (int idx = 0; idx < n; idx++) {
      int pos =
          int(std::lower_bound(xs.begin(), xs.end(), a[idx]) - xs.begin());
      rt[idx + 1] = insert(rt[idx], 0, int(xs.size()), pos);
    }
  }

  /// @brief Return the zero-indexed kth smallest value in [l, r).
  T kth(int l, int r, int k) const {
    assert(0 <= l && l <= r && r <= n);
    assert(0 <= k && k < r - l);
    int low = 0;
    int hi = int(xs.size());
    int rl = rt[l];
    int rr = rt[r];
    while (hi - low > 1) {
      int mid = std::midpoint(low, hi);
      int cl = tr[tr[rr].ls].cnt - tr[tr[rl].ls].cnt;
      if (k < cl) {
        rl = tr[rl].ls;
        rr = tr[rr].ls;
        hi = mid;
      } else {
        k -= cl;
        rl = tr[rl].rs;
        rr = tr[rr].rs;
        low = mid;
      }
    }
    return xs[low];
  }

  /// @brief Count values smaller than bound in [l, r).
  int count_less(int l, int r, const T &lim) const {
    assert(0 <= l && l <= r && r <= n);
    int pos = int(std::lower_bound(xs.begin(), xs.end(), lim) - xs.begin());
    return prefix_count(rt[r], 0, int(xs.size()), pos) -
           prefix_count(rt[l], 0, int(xs.size()), pos);
  }

  /// @brief Count values in [lo, big) within positions [l, r).
  int count(int l, int r, const T &lo, const T &big) const {
    return count_less(l, r, big) - count_less(l, r, lo);
  }

private:
  int insert(int rt0, int low, int hi, int pos) {
    int cur = int(tr.size());
    tr.push_back(tr[rt0]);
    tr[cur].cnt++;
    if (hi - low == 1) {
      return cur;
    }
    int mid = std::midpoint(low, hi);
    if (pos < mid) {
      tr[cur].ls = insert(tr[rt0].ls, low, mid, pos);
    } else {
      tr[cur].rs = insert(tr[rt0].rs, mid, hi, pos);
    }
    return cur;
  }

  int prefix_count(int rt0, int low, int hi, int r) const {
    if (rt0 == 0 || r <= low) {
      return 0;
    }
    if (hi <= r) {
      return tr[rt0].cnt;
    }
    int mid = std::midpoint(low, hi);
    return prefix_count(tr[rt0].ls, low, mid, r) +
           prefix_count(tr[rt0].rs, mid, hi, r);
  }
};

} // namespace noya