Skip to content

implicit_treap.hpp

SECTIONData Structure INCLUDEnoya/implicit_treap.hpp

把序列下标隐式存进随机平衡树,支持按位置切分、合并、插入删除与区间聚合。

Complexity: Time: Expected O(log n) per split/merge/update/query. Space: O(n) nodes and O(log n) expected stack.

AC 记录:deque, deque_operate_all_composite, range_reverse_range_sum

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: Expected O(log n) per split/merge/update/query.
/// Space: O(n) nodes and O(log n) expected stack.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <functional>
#include <vector>

namespace noya {

/// @brief Implicit randomized treap supporting insertion, erasure, reversal,
/// point updates, and ordered monoid products in expected O(log n) time.
template <class S, auto op, auto e> struct implicit_treap {
  struct node {
    S val;
    S fwd;
    S bwd;
    std::uint64_t rnd = 0;
    int ls = -1;
    int rs = -1;
    int sz = 1;
    bool rev = false;
  };

  std::vector<node> tr;
  int rt = -1;

  implicit_treap() = default;
  explicit implicit_treap(const std::vector<S> &a) {
    tr.reserve(a.size());
    for (const S &val : a) {
      rt = merge(rt, make_node(val));
    }
  }

  int size() const { return node_size(rt); }
  bool empty() const { return rt == -1; }

  /// @brief Insert value immediately before position.
  void insert(int pos, const S &val) {
    assert(0 <= pos && pos <= size());
    auto [l, r] = split(rt, pos);
    rt = merge(merge(l, make_node(val)), r);
  }

  /// @brief Erase and return the value at position.
  S erase(int pos) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    assert(mid != -1);
    S val = tr[mid].val;
    rt = merge(l, r);
    return val;
  }

  /// @brief Replace the value at position.
  void set(int pos, const S &val) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    tr[mid].val = val;
    pull(mid);
    rt = merge(l, merge(mid, r));
  }

  /// @brief Return the value at position.
  S get(int pos) {
    assert(0 <= pos && pos < size());
    int cur = rt;
    while (cur != -1) {
      push(cur);
      int lsz = node_size(tr[cur].ls);
      if (pos < lsz) {
        cur = tr[cur].ls;
      } else if (pos == lsz) {
        return tr[cur].val;
      } else {
        pos -= lsz + 1;
        cur = tr[cur].rs;
      }
    }
    assert(false);
    return e();
  }

  /// @brief Reverse the half-open range [l, r).
  void reverse(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    apply_reverse(mid);
    rt = merge(pre, merge(mid, tl));
  }

  /// @brief Return the ordered monoid product over [l, r).
  S prod(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    S res = forward_product(mid);
    rt = merge(pre, merge(mid, tl));
    return res;
  }

  /// @brief Materialize the current sequence.
  std::vector<S> to_vector() {
    std::vector<S> res;
    res.reserve(size());
    auto dfs = [&](auto &f, int cur) -> void {
      if (cur == -1) {
        return;
      }
      push(cur);
      f(f, tr[cur].ls);
      res.push_back(tr[cur].val);
      f(f, tr[cur].rs);
    };
    dfs(dfs, rt);
    return res;
  }

private:
  std::uint64_t rng = 0x243f6a8885a308d3ULL;

  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }
  S forward_product(int cur) const { return cur == -1 ? e() : tr[cur].fwd; }
  S backward_product(int cur) const { return cur == -1 ? e() : tr[cur].bwd; }

  std::uint64_t next_priority() {
    std::uint64_t val = (rng += 0x9e3779b97f4a7c15ULL);
    val = (val ^ (val >> 30)) * 0xbf58476d1ce4e5b9ULL;
    val = (val ^ (val >> 27)) * 0x94d049bb133111ebULL;
    return val ^ (val >> 31);
  }

  int make_node(const S &val) {
    tr.push_back({val, val, val, next_priority()});
    return int(tr.size()) - 1;
  }

  void apply_reverse(int cur) {
    if (cur == -1) {
      return;
    }
    std::swap(tr[cur].ls, tr[cur].rs);
    std::swap(tr[cur].fwd, tr[cur].bwd);
    tr[cur].rev = !tr[cur].rev;
  }

  void push(int cur) {
    if (cur != -1 && tr[cur].rev) {
      apply_reverse(tr[cur].ls);
      apply_reverse(tr[cur].rs);
      tr[cur].rev = false;
    }
  }

  void pull(int cur) {
    tr[cur].sz = 1 + node_size(tr[cur].ls) + node_size(tr[cur].rs);
    tr[cur].fwd = op(op(forward_product(tr[cur].ls), tr[cur].val),
                     forward_product(tr[cur].rs));
    tr[cur].bwd = op(op(backward_product(tr[cur].rs), tr[cur].val),
                     backward_product(tr[cur].ls));
  }

  std::pair<int, int> split(int cur, int lsz) {
    if (cur == -1) {
      return {-1, -1};
    }
    push(cur);
    if (node_size(tr[cur].ls) >= lsz) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[cur].ls = r;
      pull(cur);
      return {l, cur};
    }
    auto [l, r] = split(tr[cur].rs, lsz - node_size(tr[cur].ls) - 1);
    tr[cur].rs = l;
    pull(cur);
    return {cur, r};
  }

  int merge(int l, int r) {
    if (l == -1 || r == -1) {
      return l == -1 ? r : l;
    }
    if (tr[l].rnd > tr[r].rnd) {
      push(l);
      tr[l].rs = merge(tr[l].rs, r);
      pull(l);
      return l;
    }
    push(r);
    tr[r].ls = merge(l, tr[r].ls);
    pull(r);
    return r;
  }

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

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

/// @complexity Time: Expected O(log n) per split/merge/update/query.
/// Space: O(n) nodes and O(log n) expected stack.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <functional>
#include <vector>

namespace noya {

/// @brief Implicit randomized treap supporting insertion, erasure, reversal,
/// point updates, and ordered monoid products in expected O(log n) time.
template <class S, auto op, auto e> struct implicit_treap {
  struct node {
    S val;
    S fwd;
    S bwd;
    std::uint64_t rnd = 0;
    int ls = -1;
    int rs = -1;
    int sz = 1;
    bool rev = false;
  };

  std::vector<node> tr;
  int rt = -1;

  implicit_treap() = default;
  explicit implicit_treap(const std::vector<S> &a) {
    tr.reserve(a.size());
    for (const S &val : a) {
      rt = merge(rt, make_node(val));
    }
  }

  int size() const { return node_size(rt); }
  bool empty() const { return rt == -1; }

  /// @brief Insert value immediately before position.
  void insert(int pos, const S &val) {
    assert(0 <= pos && pos <= size());
    auto [l, r] = split(rt, pos);
    rt = merge(merge(l, make_node(val)), r);
  }

  /// @brief Erase and return the value at position.
  S erase(int pos) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    assert(mid != -1);
    S val = tr[mid].val;
    rt = merge(l, r);
    return val;
  }

  /// @brief Replace the value at position.
  void set(int pos, const S &val) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    tr[mid].val = val;
    pull(mid);
    rt = merge(l, merge(mid, r));
  }

  /// @brief Return the value at position.
  S get(int pos) {
    assert(0 <= pos && pos < size());
    int cur = rt;
    while (cur != -1) {
      push(cur);
      int lsz = node_size(tr[cur].ls);
      if (pos < lsz) {
        cur = tr[cur].ls;
      } else if (pos == lsz) {
        return tr[cur].val;
      } else {
        pos -= lsz + 1;
        cur = tr[cur].rs;
      }
    }
    assert(false);
    return e();
  }

  /// @brief Reverse the half-open range [l, r).
  void reverse(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    apply_reverse(mid);
    rt = merge(pre, merge(mid, tl));
  }

  /// @brief Return the ordered monoid product over [l, r).
  S prod(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    S res = forward_product(mid);
    rt = merge(pre, merge(mid, tl));
    return res;
  }

  /// @brief Materialize the current sequence.
  std::vector<S> to_vector() {
    std::vector<S> res;
    res.reserve(size());
    auto dfs = [&](auto &f, int cur) -> void {
      if (cur == -1) {
        return;
      }
      push(cur);
      f(f, tr[cur].ls);
      res.push_back(tr[cur].val);
      f(f, tr[cur].rs);
    };
    dfs(dfs, rt);
    return res;
  }

private:
  std::uint64_t rng = 0x243f6a8885a308d3ULL;

  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }
  S forward_product(int cur) const { return cur == -1 ? e() : tr[cur].fwd; }
  S backward_product(int cur) const { return cur == -1 ? e() : tr[cur].bwd; }

  std::uint64_t next_priority() {
    std::uint64_t val = (rng += 0x9e3779b97f4a7c15ULL);
    val = (val ^ (val >> 30)) * 0xbf58476d1ce4e5b9ULL;
    val = (val ^ (val >> 27)) * 0x94d049bb133111ebULL;
    return val ^ (val >> 31);
  }

  int make_node(const S &val) {
    tr.push_back({val, val, val, next_priority()});
    return int(tr.size()) - 1;
  }

  void apply_reverse(int cur) {
    if (cur == -1) {
      return;
    }
    std::swap(tr[cur].ls, tr[cur].rs);
    std::swap(tr[cur].fwd, tr[cur].bwd);
    tr[cur].rev = !tr[cur].rev;
  }

  void push(int cur) {
    if (cur != -1 && tr[cur].rev) {
      apply_reverse(tr[cur].ls);
      apply_reverse(tr[cur].rs);
      tr[cur].rev = false;
    }
  }

  void pull(int cur) {
    tr[cur].sz = 1 + node_size(tr[cur].ls) + node_size(tr[cur].rs);
    tr[cur].fwd = op(op(forward_product(tr[cur].ls), tr[cur].val),
                     forward_product(tr[cur].rs));
    tr[cur].bwd = op(op(backward_product(tr[cur].rs), tr[cur].val),
                     backward_product(tr[cur].ls));
  }

  std::pair<int, int> split(int cur, int lsz) {
    if (cur == -1) {
      return {-1, -1};
    }
    push(cur);
    if (node_size(tr[cur].ls) >= lsz) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[cur].ls = r;
      pull(cur);
      return {l, cur};
    }
    auto [l, r] = split(tr[cur].rs, lsz - node_size(tr[cur].ls) - 1);
    tr[cur].rs = l;
    pull(cur);
    return {cur, r};
  }

  int merge(int l, int r) {
    if (l == -1 || r == -1) {
      return l == -1 ? r : l;
    }
    if (tr[l].rnd > tr[r].rnd) {
      push(l);
      tr[l].rs = merge(tr[l].rs, r);
      pull(l);
      return l;
    }
    push(r);
    tr[r].ls = merge(l, tr[r].ls);
    pull(r);
    return r;
  }

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

} // namespace noya

#endif // NOYA_IMPLICIT_TREAP_HPP
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <functional>
#include <vector>

/// @complexity Time: Expected O(log n) per split/merge/update/query.
/// Space: O(n) nodes and O(log n) expected stack.

namespace noya {

/// @brief Implicit randomized treap supporting insertion, erasure, reversal,
/// point updates, and ordered monoid products in expected O(log n) time.
template <class S, auto op, auto e> struct implicit_treap {
  struct node {
    S val;
    S fwd;
    S bwd;
    std::uint64_t rnd = 0;
    int ls = -1;
    int rs = -1;
    int sz = 1;
    bool rev = false;
  };

  std::vector<node> tr;
  int rt = -1;

  implicit_treap() = default;
  explicit implicit_treap(const std::vector<S> &a) {
    tr.reserve(a.size());
    for (const S &val : a) {
      rt = merge(rt, make_node(val));
    }
  }

  int size() const { return node_size(rt); }
  bool empty() const { return rt == -1; }

  /// @brief Insert value immediately before position.
  void insert(int pos, const S &val) {
    assert(0 <= pos && pos <= size());
    auto [l, r] = split(rt, pos);
    rt = merge(merge(l, make_node(val)), r);
  }

  /// @brief Erase and return the value at position.
  S erase(int pos) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    assert(mid != -1);
    S val = tr[mid].val;
    rt = merge(l, r);
    return val;
  }

  /// @brief Replace the value at position.
  void set(int pos, const S &val) {
    assert(0 <= pos && pos < size());
    auto [l, suf] = split(rt, pos);
    auto [mid, r] = split(suf, 1);
    tr[mid].val = val;
    pull(mid);
    rt = merge(l, merge(mid, r));
  }

  /// @brief Return the value at position.
  S get(int pos) {
    assert(0 <= pos && pos < size());
    int cur = rt;
    while (cur != -1) {
      push(cur);
      int lsz = node_size(tr[cur].ls);
      if (pos < lsz) {
        cur = tr[cur].ls;
      } else if (pos == lsz) {
        return tr[cur].val;
      } else {
        pos -= lsz + 1;
        cur = tr[cur].rs;
      }
    }
    assert(false);
    return e();
  }

  /// @brief Reverse the half-open range [l, r).
  void reverse(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    apply_reverse(mid);
    rt = merge(pre, merge(mid, tl));
  }

  /// @brief Return the ordered monoid product over [l, r).
  S prod(int l, int r) {
    check_range(l, r);
    auto [pre, suf] = split(rt, l);
    auto [mid, tl] = split(suf, r - l);
    S res = forward_product(mid);
    rt = merge(pre, merge(mid, tl));
    return res;
  }

  /// @brief Materialize the current sequence.
  std::vector<S> to_vector() {
    std::vector<S> res;
    res.reserve(size());
    auto dfs = [&](auto &f, int cur) -> void {
      if (cur == -1) {
        return;
      }
      push(cur);
      f(f, tr[cur].ls);
      res.push_back(tr[cur].val);
      f(f, tr[cur].rs);
    };
    dfs(dfs, rt);
    return res;
  }

private:
  std::uint64_t rng = 0x243f6a8885a308d3ULL;

  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }
  S forward_product(int cur) const { return cur == -1 ? e() : tr[cur].fwd; }
  S backward_product(int cur) const { return cur == -1 ? e() : tr[cur].bwd; }

  std::uint64_t next_priority() {
    std::uint64_t val = (rng += 0x9e3779b97f4a7c15ULL);
    val = (val ^ (val >> 30)) * 0xbf58476d1ce4e5b9ULL;
    val = (val ^ (val >> 27)) * 0x94d049bb133111ebULL;
    return val ^ (val >> 31);
  }

  int make_node(const S &val) {
    tr.push_back({val, val, val, next_priority()});
    return int(tr.size()) - 1;
  }

  void apply_reverse(int cur) {
    if (cur == -1) {
      return;
    }
    std::swap(tr[cur].ls, tr[cur].rs);
    std::swap(tr[cur].fwd, tr[cur].bwd);
    tr[cur].rev = !tr[cur].rev;
  }

  void push(int cur) {
    if (cur != -1 && tr[cur].rev) {
      apply_reverse(tr[cur].ls);
      apply_reverse(tr[cur].rs);
      tr[cur].rev = false;
    }
  }

  void pull(int cur) {
    tr[cur].sz = 1 + node_size(tr[cur].ls) + node_size(tr[cur].rs);
    tr[cur].fwd = op(op(forward_product(tr[cur].ls), tr[cur].val),
                     forward_product(tr[cur].rs));
    tr[cur].bwd = op(op(backward_product(tr[cur].rs), tr[cur].val),
                     backward_product(tr[cur].ls));
  }

  std::pair<int, int> split(int cur, int lsz) {
    if (cur == -1) {
      return {-1, -1};
    }
    push(cur);
    if (node_size(tr[cur].ls) >= lsz) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[cur].ls = r;
      pull(cur);
      return {l, cur};
    }
    auto [l, r] = split(tr[cur].rs, lsz - node_size(tr[cur].ls) - 1);
    tr[cur].rs = l;
    pull(cur);
    return {cur, r};
  }

  int merge(int l, int r) {
    if (l == -1 || r == -1) {
      return l == -1 ? r : l;
    }
    if (tr[l].rnd > tr[r].rnd) {
      push(l);
      tr[l].rs = merge(tr[l].rs, r);
      pull(l);
      return l;
    }
    push(r);
    tr[r].ls = merge(l, tr[r].ls);
    pull(r);
    return r;
  }

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

} // namespace noya