Skip to content

sortable_segment_tree.hpp

SECTIONData Structure INCLUDEnoya/sortable_segment_tree.hpp

维护序列的单点修改、区间升降序排序和区间复合查询;适合排序后仍需按顺序聚合函数或矩阵的题目。

Complexity: Time: O(n log K) construction; a sequence of q point updates, range products, and range sorts takes O((n + q)(log n + log K)) amortized, where keys lie in [0, K). Space: O(n log K).

AC 记录:point_set_range_sort_range_composite

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n log K) construction; a sequence of q point updates,
/// range products, and range sorts takes O((n + q)(log n + log K)) amortized,
/// where keys lie in [0, K). Space: O(n log K).

#include "noya/fastset.hpp"
#include "noya/segtree.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <utility>
#include <vector>

namespace noya {

/// @brief Maintain a sequence of distinct integer keys and monoid values under
/// point replacement, ordered range product, and sorting a range by key. Each
/// maximal already-sorted block is stored as a sparse segment tree over key
/// space, containing both forward and backward products. Sorting joins all
/// blocks in the range; splitting a block by sequence rank restores query
/// boundaries. A fast set tracks block starts and an outer segment tree stores
/// one aggregate per block. Since an operation creates only O(1) boundaries,
/// the total number of block splits and merges is linear in the operation
/// count; periodic rebuilding bounds the persistent split-node storage.
template <class Monoid> class sortable_segment_tree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    value_type fwd;
    value_type bwd;
    int sz = 1;
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int m0 = 0;
  int lg = 0;
  std::size_t lim = 0;
  fast_set ss;
  segtree<Monoid> seg;
  std::vector<bool> rev;
  std::vector<int> rt;
  std::vector<node> tr;

public:
  sortable_segment_tree() = default;

  sortable_segment_tree(int m, const std::vector<int> &k,
                        const std::vector<value_type> &a) {
    build(m, k, a);
  }

  void build(int m, const std::vector<int> &k,
             const std::vector<value_type> &a) {
    assert(!k.empty());
    assert(k.size() == a.size());
    assert(m > 0);
    n = int(k.size());
    m0 = m;
    lg = 0;
    for (int rng = 1; rng < m0;) {
      rng <<= 1;
      lg++;
    }
    lim = std::max<std::size_t>(4096, std::size_t(n) * std::size_t(lg + 1) * 2 +
                                          1024);
    initialize(k, a);
  }

  int size() const { return n; }

  void set(int pos, int key, const value_type &val) {
    assert(0 <= pos && pos < n);
    assert(0 <= key && key < m0);
    make_boundary(pos);
    make_boundary(pos + 1);
    maybe_rebuild();
    rev[pos] = false;
    rt[pos] = make_node();
    set_key(rt[pos], 0, m0, key, val);
    seg.set(pos, val);
  }

  value_type prod(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return Monoid::unit();
    }
    make_boundary(l);
    make_boundary(r);
    return seg.prod(l, r);
  }

  value_type all_prod() const { return seg.all_prod(); }

  void sort_ascending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    make_boundary(l);
    make_boundary(r);
    while (true) {
      maybe_rebuild();
      int nl = ss.next(l + 1);
      if (nl == r) {
        break;
      }
      rt[l] = merge(rt[l], rt[nl]);
      ss.erase(nl);
      seg.set(nl, Monoid::unit());
    }
    rev[l] = false;
    seg.set(l, forward_product(rt[l]));
  }

  void sort_descending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    sort_ascending(l, r);
    rev[l] = true;
    seg.set(l, backward_product(rt[l]));
  }

private:
  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }

  value_type forward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].fwd;
  }

  value_type backward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].bwd;
  }

  int make_node(value_type val = Monoid::unit()) {
    tr.push_back({val, val, 1, -1, -1});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    if (l == -1 && r == -1) {
      return;
    }
    tr[cur].sz = node_size(l) + node_size(r);
    tr[cur].fwd = Monoid::op(forward_product(l), forward_product(r));
    tr[cur].bwd = Monoid::op(backward_product(r), backward_product(l));
  }

  void set_key(int cur, int low, int hi, int key, const value_type &val) {
    if (low + 1 == hi) {
      tr[cur].fwd = tr[cur].bwd = val;
      return;
    }
    int mid = (low + hi) / 2;
    if (key < mid) {
      if (tr[cur].ls == -1) {
        tr[cur].ls = make_node();
      }
      set_key(tr[cur].ls, low, mid, key, val);
    } else {
      if (tr[cur].rs == -1) {
        tr[cur].rs = make_node();
      }
      set_key(tr[cur].rs, mid, hi, key, val);
    }
    pull(cur);
  }

  int merge(int arr, int b) {
    if (arr == -1 || b == -1) {
      return arr == -1 ? b : arr;
    }
    tr[arr].ls = merge(tr[arr].ls, tr[b].ls);
    tr[arr].rs = merge(tr[arr].rs, tr[b].rs);
    pull(arr);
    return arr;
  }

  std::pair<int, int> split(int cur, int lsz) {
    assert(cur != -1);
    assert(0 <= lsz && lsz <= tr[cur].sz);
    if (lsz == 0) {
      return {-1, cur};
    }
    if (lsz == tr[cur].sz) {
      return {cur, -1};
    }
    int rp = make_node();
    int cl = node_size(tr[cur].ls);
    if (lsz <= cl) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[rp].ls = r;
      tr[rp].rs = tr[cur].rs;
      tr[cur].ls = l;
      tr[cur].rs = -1;
    } else {
      auto [l, r] = split(tr[cur].rs, lsz - cl);
      tr[cur].rs = l;
      tr[rp].rs = r;
    }
    pull(cur);
    pull(rp);
    return {cur, rp};
  }

  void split_at(int pos) {
    if (pos == n || ss.contains(pos)) {
      return;
    }
    int st = ss.prev(pos);
    int ed = ss.next(st + 1);
    assert(st >= 0 && ed > pos);
    ss.insert(pos);
    if (!rev[st]) {
      auto [l, r] = split(rt[st], pos - st);
      rt[st] = l;
      rt[pos] = r;
      rev[st] = rev[pos] = false;
      seg.set(st, forward_product(l));
      seg.set(pos, forward_product(r));
    } else {
      auto [kl, kh] = split(rt[st], ed - pos);
      rt[st] = kh;
      rt[pos] = kl;
      rev[st] = rev[pos] = true;
      seg.set(st, backward_product(kh));
      seg.set(pos, backward_product(kl));
    }
  }

  void make_boundary(int pos) {
    maybe_rebuild();
    split_at(pos);
  }

  void maybe_rebuild() {
    if (tr.size() * 10 > lim * 9) {
      rebuild();
    }
  }

  void materialize(int cur, int low, int hi, bool re0, std::vector<int> &k,
                   std::vector<value_type> &a) const {
    if (cur == -1) {
      return;
    }
    if (low + 1 == hi) {
      k.push_back(low);
      a.push_back(tr[cur].fwd);
      return;
    }
    int mid = (low + hi) / 2;
    if (!re0) {
      materialize(tr[cur].ls, low, mid, false, k, a);
      materialize(tr[cur].rs, mid, hi, false, k, a);
    } else {
      materialize(tr[cur].rs, mid, hi, true, k, a);
      materialize(tr[cur].ls, low, mid, true, k, a);
    }
  }

  void rebuild() {
    std::vector<int> k;
    std::vector<value_type> a;
    k.reserve(n);
    a.reserve(n);
    for (int st = ss.next(0); st < n; st = ss.next(st + 1)) {
      materialize(rt[st], 0, m0, rev[st], k, a);
    }
    assert(int(k.size()) == n);
    initialize(k, a);
  }

  void initialize(const std::vector<int> &k, const std::vector<value_type> &a) {
    tr.clear();
    ss = fast_set(n);
    seg = segtree<Monoid>(a);
    rev.assign(n, false);
    rt.assign(n, -1);
    for (int pos = 0; pos < n; pos++) {
      assert(0 <= k[pos] && k[pos] < m0);
      ss.insert(pos);
      rt[pos] = make_node();
      set_key(rt[pos], 0, m0, k[pos], a[pos]);
    }
  }
};

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

/// @complexity Time: O(n log K) construction; a sequence of q point updates,
/// range products, and range sorts takes O((n + q)(log n + log K)) amortized,
/// where keys lie in [0, K). Space: O(n log K).

#include "noya/fastset.hpp"
#include "noya/segtree.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <utility>
#include <vector>

namespace noya {

/// @brief Maintain a sequence of distinct integer keys and monoid values under
/// point replacement, ordered range product, and sorting a range by key. Each
/// maximal already-sorted block is stored as a sparse segment tree over key
/// space, containing both forward and backward products. Sorting joins all
/// blocks in the range; splitting a block by sequence rank restores query
/// boundaries. A fast set tracks block starts and an outer segment tree stores
/// one aggregate per block. Since an operation creates only O(1) boundaries,
/// the total number of block splits and merges is linear in the operation
/// count; periodic rebuilding bounds the persistent split-node storage.
template <class Monoid> class sortable_segment_tree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    value_type fwd;
    value_type bwd;
    int sz = 1;
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int m0 = 0;
  int lg = 0;
  std::size_t lim = 0;
  fast_set ss;
  segtree<Monoid> seg;
  std::vector<bool> rev;
  std::vector<int> rt;
  std::vector<node> tr;

public:
  sortable_segment_tree() = default;

  sortable_segment_tree(int m, const std::vector<int> &k,
                        const std::vector<value_type> &a) {
    build(m, k, a);
  }

  void build(int m, const std::vector<int> &k,
             const std::vector<value_type> &a) {
    assert(!k.empty());
    assert(k.size() == a.size());
    assert(m > 0);
    n = int(k.size());
    m0 = m;
    lg = 0;
    for (int rng = 1; rng < m0;) {
      rng <<= 1;
      lg++;
    }
    lim = std::max<std::size_t>(4096, std::size_t(n) * std::size_t(lg + 1) * 2 +
                                          1024);
    initialize(k, a);
  }

  int size() const { return n; }

  void set(int pos, int key, const value_type &val) {
    assert(0 <= pos && pos < n);
    assert(0 <= key && key < m0);
    make_boundary(pos);
    make_boundary(pos + 1);
    maybe_rebuild();
    rev[pos] = false;
    rt[pos] = make_node();
    set_key(rt[pos], 0, m0, key, val);
    seg.set(pos, val);
  }

  value_type prod(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return Monoid::unit();
    }
    make_boundary(l);
    make_boundary(r);
    return seg.prod(l, r);
  }

  value_type all_prod() const { return seg.all_prod(); }

  void sort_ascending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    make_boundary(l);
    make_boundary(r);
    while (true) {
      maybe_rebuild();
      int nl = ss.next(l + 1);
      if (nl == r) {
        break;
      }
      rt[l] = merge(rt[l], rt[nl]);
      ss.erase(nl);
      seg.set(nl, Monoid::unit());
    }
    rev[l] = false;
    seg.set(l, forward_product(rt[l]));
  }

  void sort_descending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    sort_ascending(l, r);
    rev[l] = true;
    seg.set(l, backward_product(rt[l]));
  }

private:
  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }

  value_type forward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].fwd;
  }

  value_type backward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].bwd;
  }

  int make_node(value_type val = Monoid::unit()) {
    tr.push_back({val, val, 1, -1, -1});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    if (l == -1 && r == -1) {
      return;
    }
    tr[cur].sz = node_size(l) + node_size(r);
    tr[cur].fwd = Monoid::op(forward_product(l), forward_product(r));
    tr[cur].bwd = Monoid::op(backward_product(r), backward_product(l));
  }

  void set_key(int cur, int low, int hi, int key, const value_type &val) {
    if (low + 1 == hi) {
      tr[cur].fwd = tr[cur].bwd = val;
      return;
    }
    int mid = (low + hi) / 2;
    if (key < mid) {
      if (tr[cur].ls == -1) {
        tr[cur].ls = make_node();
      }
      set_key(tr[cur].ls, low, mid, key, val);
    } else {
      if (tr[cur].rs == -1) {
        tr[cur].rs = make_node();
      }
      set_key(tr[cur].rs, mid, hi, key, val);
    }
    pull(cur);
  }

  int merge(int arr, int b) {
    if (arr == -1 || b == -1) {
      return arr == -1 ? b : arr;
    }
    tr[arr].ls = merge(tr[arr].ls, tr[b].ls);
    tr[arr].rs = merge(tr[arr].rs, tr[b].rs);
    pull(arr);
    return arr;
  }

  std::pair<int, int> split(int cur, int lsz) {
    assert(cur != -1);
    assert(0 <= lsz && lsz <= tr[cur].sz);
    if (lsz == 0) {
      return {-1, cur};
    }
    if (lsz == tr[cur].sz) {
      return {cur, -1};
    }
    int rp = make_node();
    int cl = node_size(tr[cur].ls);
    if (lsz <= cl) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[rp].ls = r;
      tr[rp].rs = tr[cur].rs;
      tr[cur].ls = l;
      tr[cur].rs = -1;
    } else {
      auto [l, r] = split(tr[cur].rs, lsz - cl);
      tr[cur].rs = l;
      tr[rp].rs = r;
    }
    pull(cur);
    pull(rp);
    return {cur, rp};
  }

  void split_at(int pos) {
    if (pos == n || ss.contains(pos)) {
      return;
    }
    int st = ss.prev(pos);
    int ed = ss.next(st + 1);
    assert(st >= 0 && ed > pos);
    ss.insert(pos);
    if (!rev[st]) {
      auto [l, r] = split(rt[st], pos - st);
      rt[st] = l;
      rt[pos] = r;
      rev[st] = rev[pos] = false;
      seg.set(st, forward_product(l));
      seg.set(pos, forward_product(r));
    } else {
      auto [kl, kh] = split(rt[st], ed - pos);
      rt[st] = kh;
      rt[pos] = kl;
      rev[st] = rev[pos] = true;
      seg.set(st, backward_product(kh));
      seg.set(pos, backward_product(kl));
    }
  }

  void make_boundary(int pos) {
    maybe_rebuild();
    split_at(pos);
  }

  void maybe_rebuild() {
    if (tr.size() * 10 > lim * 9) {
      rebuild();
    }
  }

  void materialize(int cur, int low, int hi, bool re0, std::vector<int> &k,
                   std::vector<value_type> &a) const {
    if (cur == -1) {
      return;
    }
    if (low + 1 == hi) {
      k.push_back(low);
      a.push_back(tr[cur].fwd);
      return;
    }
    int mid = (low + hi) / 2;
    if (!re0) {
      materialize(tr[cur].ls, low, mid, false, k, a);
      materialize(tr[cur].rs, mid, hi, false, k, a);
    } else {
      materialize(tr[cur].rs, mid, hi, true, k, a);
      materialize(tr[cur].ls, low, mid, true, k, a);
    }
  }

  void rebuild() {
    std::vector<int> k;
    std::vector<value_type> a;
    k.reserve(n);
    a.reserve(n);
    for (int st = ss.next(0); st < n; st = ss.next(st + 1)) {
      materialize(rt[st], 0, m0, rev[st], k, a);
    }
    assert(int(k.size()) == n);
    initialize(k, a);
  }

  void initialize(const std::vector<int> &k, const std::vector<value_type> &a) {
    tr.clear();
    ss = fast_set(n);
    seg = segtree<Monoid>(a);
    rev.assign(n, false);
    rt.assign(n, -1);
    for (int pos = 0; pos < n; pos++) {
      assert(0 <= k[pos] && k[pos] < m0);
      ss.insert(pos);
      rt[pos] = make_node();
      set_key(rt[pos], 0, m0, k[pos], a[pos]);
    }
  }
};

} // namespace noya

#endif // NOYA_SORTABLE_SEGMENT_TREE_HPP
#include <algorithm>
#include <assert.h>
#include <cassert>
#include <cstddef>
#include <utility>
#include <vector>

/// @complexity Time: O(n log K) construction; a sequence of q point updates,
/// range products, and range sorts takes O((n + q)(log n + log K)) amortized,
/// where keys lie in [0, K). Space: O(n log K).

/// @complexity Time: O(log_64 n) predecessor/successor operations.
/// Space: O(n / 64).
namespace noya {
/// @brief Fixed-universe ordered set implemented as a hierarchy of bitsets.
/// Level zero marks present keys and every higher level marks nonempty words
/// below it. A predecessor or successor first scans one machine word, climbs
/// until it finds a nonempty sibling, then descends through extreme set bits.
struct fast_set {
  // max{ceil(log_64(n)), 1}
  int lg, n;
  std::vector<unsigned long long> a[6];
  explicit fast_set(int n_ = 0) : n(n_) {
    assert(n >= 0);
    int m = n ? n : 1;
    for (int d = 0;; ++d) {
      m = (m + 63) >> 6;
      a[d].assign(m, 0);
      if (m == 1) {
        lg = d + 1;
        break;
      }
    }
  }
  bool empty() const { return !a[lg - 1][0]; }
  bool contains(int x) const { return (a[0][x >> 6] >> (x & 63)) & 1; }
  void insert(int x) {
    for (int d = 0; d < lg; ++d) {
      const int q = x >> 6, r = x & 63;
      a[d][q] |= 1ULL << r;
      x = q;
    }
  }
  void erase(int x) {
    for (int d = 0; d < lg; ++d) {
      const int q = x >> 6, r = x & 63;
      if ((a[d][q] &= ~(1ULL << r)))
        break;
      x = q;
    }
  }
  /// @brief Find max element <= x, or -1 if none.
  int prev(int x) const {
    if (x > n - 1)
      x = n - 1;
    for (int d = 0; d <= lg; ++d) {
      if (x < 0)
        break;
      const int q = x >> 6, r = x & 63;
      const unsigned long long lo = a[d][q] << (63 - r);
      if (lo) {
        x -= __builtin_clzll(lo);
        for (int e = d; --e >= 0;)
          x = x << 6 | (63 - __builtin_clzll(a[e][x]));
        return x;
      }
      x = q - 1;
    }
    return -1;
  }
  /// @brief Find min element >= x, or n if none.
  int next(int x) const {
    if (x < 0)
      x = 0;
    for (int d = 0; d < lg; ++d) {
      const int q = x >> 6, r = x & 63;
      if (static_cast<unsigned>(q) >= a[d].size())
        break;
      const unsigned long long hi = a[d][q] >> r;
      if (hi) {
        x += __builtin_ctzll(hi);
        for (int e = d; --e >= 0;)
          x = x << 6 | __builtin_ctzll(a[e][x]);
        return x;
      }
      x = q + 1;
    }
    return n;
  }
};

template <class T> struct painter {
  int n;
  fast_set s;
  std::vector<T> ts;
  painter() {}
  painter(int n_, const T &t) : n(n_), s(n + 1), ts(n + 2, t) {}
  template <class F> void paint(int a, int b, const T &t, F f) {
    assert(0 <= a);
    assert(a <= b);
    assert(b <= n);
    if (a == b)
      return;
    // auto it = this->lower_bound(a);
    int c = s.next(a);
    if (b < c) {
      f(a, b, ts[c]);
      s.insert(a);
      ts[a] = ts[c];
      s.insert(b);
      ts[b] = t;
    } else if (a < c) {
      const T ta = ts[c];
      int k = a;
      for (; c <= b; s.erase(c), c = s.next(c)) {
        f(k, c, ts[c]);
        k = c;
      }
      if (k < b) {
        f(k, b, ts[c]);
      }
      s.insert(a);
      ts[a] = ta;
      s.insert(b);
      ts[b] = t;
    } else {
      c = s.next(c + 1);
      int k = a;
      for (; c <= b; s.erase(c), c = s.next(c)) {
        f(k, c, ts[c]);
        k = c;
      }
      if (k < b) {
        f(k, b, ts[c]);
      }
      s.insert(b);
      ts[b] = t;
    }
  }
  void paint(int a, int b, const T &t) {
    paint(a, b, t, [&](int, int, const T &) -> void {});
  }
  T get(int k) const {
    assert(0 <= k);
    assert(k < n);
    return ts[s.next(k + 1)];
  }
};
} // namespace noya

/// @complexity Time: O(n) build and O(log n) point update/range product.
/// Space: O(n).

namespace noya {

/// @brief Segment tree for a monoid type.
/// Monoid must provide `using value_type`, `value_type unit()`, and
/// `value_type op(value_type, value_type)`.
template <class Monoid> struct segtree {
  using MX = Monoid;
  using S = typename MX::value_type;
  using value_type = S;

  int n = 0;
  int sz = 1;
  std::vector<S> d;

  segtree() : segtree(0) {}
  explicit segtree(int _n) { build(_n); }

  explicit segtree(const std::vector<S> &v) { build(v); }

  template <class F> segtree(int _n, F f) { build(_n, f); }

  void build(int _n) { build(_n, [](int) { return MX::unit(); }); }

  void build(const std::vector<S> &v) {
    build(int(v.size()), [&](int i) { return v[i]; });
  }

  template <class F> void build(int _n, F f) {
    n = _n;
    sz = 1;
    while (sz < n)
      sz <<= 1;
    d.assign(sz << 1, MX::unit());
    for (int i = 0; i < n; i++)
      d[sz + i] = f(i);
    for (int i = sz - 1; i >= 1; i--)
      update(i);
  }

  void set(int p, S x) {
    assert(0 <= p && p < n);
    p += sz;
    d[p] = x;
    while (p >>= 1)
      update(p);
  }

  void multiply(int p, S x) {
    assert(0 <= p && p < n);
    p += sz;
    d[p] = MX::op(d[p], x);
    while (p >>= 1)
      update(p);
  }

  S get(int p) const {
    assert(0 <= p && p < n);
    return d[p + sz];
  }

  std::vector<S> get_all() const {
    return std::vector<S>(d.begin() + sz, d.begin() + sz + n);
  }

  S prod(int l, int r) const {
    assert(0 <= l && l <= r && r <= n);
    S sml = MX::unit(), smr = MX::unit();
    l += sz;
    r += sz;
    while (l < r) {
      if (l & 1)
        sml = MX::op(sml, d[l++]);
      if (r & 1)
        smr = MX::op(d[--r], smr);
      l >>= 1;
      r >>= 1;
    }
    return MX::op(sml, smr);
  }

  S all_prod() const { return d[1]; }

  template <class F> int max_right(int l, F f) const {
    assert(0 <= l && l <= n);
    assert(f(MX::unit()));
    if (l == n)
      return n;
    l += sz;
    S sm = MX::unit();
    do {
      while ((l & 1) == 0)
        l >>= 1;
      if (!f(MX::op(sm, d[l]))) {
        while (l < sz) {
          l <<= 1;
          if (f(MX::op(sm, d[l]))) {
            sm = MX::op(sm, d[l]);
            l++;
          }
        }
        return l - sz;
      }
      sm = MX::op(sm, d[l++]);
    } while ((l & -l) != l);
    return n;
  }

  template <class F> int min_left(int r, F f) const {
    assert(0 <= r && r <= n);
    assert(f(MX::unit()));
    if (r == 0)
      return 0;
    r += sz;
    S sm = MX::unit();
    do {
      --r;
      while (r > 1 && (r & 1))
        r >>= 1;
      if (!f(MX::op(d[r], sm))) {
        while (r < sz) {
          r = (r << 1) | 1;
          if (f(MX::op(d[r], sm))) {
            sm = MX::op(d[r], sm);
            --r;
          }
        }
        return r + 1 - sz;
      }
      sm = MX::op(d[r], sm);
    } while ((r & -r) != r);
    return 0;
  }

private:
  void update(int k) { d[k] = MX::op(d[k << 1], d[k << 1 | 1]); }
};

} // namespace noya

namespace noya {

/// @brief Maintain a sequence of distinct integer keys and monoid values under
/// point replacement, ordered range product, and sorting a range by key. Each
/// maximal already-sorted block is stored as a sparse segment tree over key
/// space, containing both forward and backward products. Sorting joins all
/// blocks in the range; splitting a block by sequence rank restores query
/// boundaries. A fast set tracks block starts and an outer segment tree stores
/// one aggregate per block. Since an operation creates only O(1) boundaries,
/// the total number of block splits and merges is linear in the operation
/// count; periodic rebuilding bounds the persistent split-node storage.
template <class Monoid> class sortable_segment_tree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    value_type fwd;
    value_type bwd;
    int sz = 1;
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int m0 = 0;
  int lg = 0;
  std::size_t lim = 0;
  fast_set ss;
  segtree<Monoid> seg;
  std::vector<bool> rev;
  std::vector<int> rt;
  std::vector<node> tr;

public:
  sortable_segment_tree() = default;

  sortable_segment_tree(int m, const std::vector<int> &k,
                        const std::vector<value_type> &a) {
    build(m, k, a);
  }

  void build(int m, const std::vector<int> &k,
             const std::vector<value_type> &a) {
    assert(!k.empty());
    assert(k.size() == a.size());
    assert(m > 0);
    n = int(k.size());
    m0 = m;
    lg = 0;
    for (int rng = 1; rng < m0;) {
      rng <<= 1;
      lg++;
    }
    lim = std::max<std::size_t>(4096, std::size_t(n) * std::size_t(lg + 1) * 2 +
                                          1024);
    initialize(k, a);
  }

  int size() const { return n; }

  void set(int pos, int key, const value_type &val) {
    assert(0 <= pos && pos < n);
    assert(0 <= key && key < m0);
    make_boundary(pos);
    make_boundary(pos + 1);
    maybe_rebuild();
    rev[pos] = false;
    rt[pos] = make_node();
    set_key(rt[pos], 0, m0, key, val);
    seg.set(pos, val);
  }

  value_type prod(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return Monoid::unit();
    }
    make_boundary(l);
    make_boundary(r);
    return seg.prod(l, r);
  }

  value_type all_prod() const { return seg.all_prod(); }

  void sort_ascending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    make_boundary(l);
    make_boundary(r);
    while (true) {
      maybe_rebuild();
      int nl = ss.next(l + 1);
      if (nl == r) {
        break;
      }
      rt[l] = merge(rt[l], rt[nl]);
      ss.erase(nl);
      seg.set(nl, Monoid::unit());
    }
    rev[l] = false;
    seg.set(l, forward_product(rt[l]));
  }

  void sort_descending(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return;
    }
    sort_ascending(l, r);
    rev[l] = true;
    seg.set(l, backward_product(rt[l]));
  }

private:
  int node_size(int cur) const { return cur == -1 ? 0 : tr[cur].sz; }

  value_type forward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].fwd;
  }

  value_type backward_product(int cur) const {
    return cur == -1 ? Monoid::unit() : tr[cur].bwd;
  }

  int make_node(value_type val = Monoid::unit()) {
    tr.push_back({val, val, 1, -1, -1});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    if (l == -1 && r == -1) {
      return;
    }
    tr[cur].sz = node_size(l) + node_size(r);
    tr[cur].fwd = Monoid::op(forward_product(l), forward_product(r));
    tr[cur].bwd = Monoid::op(backward_product(r), backward_product(l));
  }

  void set_key(int cur, int low, int hi, int key, const value_type &val) {
    if (low + 1 == hi) {
      tr[cur].fwd = tr[cur].bwd = val;
      return;
    }
    int mid = (low + hi) / 2;
    if (key < mid) {
      if (tr[cur].ls == -1) {
        tr[cur].ls = make_node();
      }
      set_key(tr[cur].ls, low, mid, key, val);
    } else {
      if (tr[cur].rs == -1) {
        tr[cur].rs = make_node();
      }
      set_key(tr[cur].rs, mid, hi, key, val);
    }
    pull(cur);
  }

  int merge(int arr, int b) {
    if (arr == -1 || b == -1) {
      return arr == -1 ? b : arr;
    }
    tr[arr].ls = merge(tr[arr].ls, tr[b].ls);
    tr[arr].rs = merge(tr[arr].rs, tr[b].rs);
    pull(arr);
    return arr;
  }

  std::pair<int, int> split(int cur, int lsz) {
    assert(cur != -1);
    assert(0 <= lsz && lsz <= tr[cur].sz);
    if (lsz == 0) {
      return {-1, cur};
    }
    if (lsz == tr[cur].sz) {
      return {cur, -1};
    }
    int rp = make_node();
    int cl = node_size(tr[cur].ls);
    if (lsz <= cl) {
      auto [l, r] = split(tr[cur].ls, lsz);
      tr[rp].ls = r;
      tr[rp].rs = tr[cur].rs;
      tr[cur].ls = l;
      tr[cur].rs = -1;
    } else {
      auto [l, r] = split(tr[cur].rs, lsz - cl);
      tr[cur].rs = l;
      tr[rp].rs = r;
    }
    pull(cur);
    pull(rp);
    return {cur, rp};
  }

  void split_at(int pos) {
    if (pos == n || ss.contains(pos)) {
      return;
    }
    int st = ss.prev(pos);
    int ed = ss.next(st + 1);
    assert(st >= 0 && ed > pos);
    ss.insert(pos);
    if (!rev[st]) {
      auto [l, r] = split(rt[st], pos - st);
      rt[st] = l;
      rt[pos] = r;
      rev[st] = rev[pos] = false;
      seg.set(st, forward_product(l));
      seg.set(pos, forward_product(r));
    } else {
      auto [kl, kh] = split(rt[st], ed - pos);
      rt[st] = kh;
      rt[pos] = kl;
      rev[st] = rev[pos] = true;
      seg.set(st, backward_product(kh));
      seg.set(pos, backward_product(kl));
    }
  }

  void make_boundary(int pos) {
    maybe_rebuild();
    split_at(pos);
  }

  void maybe_rebuild() {
    if (tr.size() * 10 > lim * 9) {
      rebuild();
    }
  }

  void materialize(int cur, int low, int hi, bool re0, std::vector<int> &k,
                   std::vector<value_type> &a) const {
    if (cur == -1) {
      return;
    }
    if (low + 1 == hi) {
      k.push_back(low);
      a.push_back(tr[cur].fwd);
      return;
    }
    int mid = (low + hi) / 2;
    if (!re0) {
      materialize(tr[cur].ls, low, mid, false, k, a);
      materialize(tr[cur].rs, mid, hi, false, k, a);
    } else {
      materialize(tr[cur].rs, mid, hi, true, k, a);
      materialize(tr[cur].ls, low, mid, true, k, a);
    }
  }

  void rebuild() {
    std::vector<int> k;
    std::vector<value_type> a;
    k.reserve(n);
    a.reserve(n);
    for (int st = ss.next(0); st < n; st = ss.next(st + 1)) {
      materialize(rt[st], 0, m0, rev[st], k, a);
    }
    assert(int(k.size()) == n);
    initialize(k, a);
  }

  void initialize(const std::vector<int> &k, const std::vector<value_type> &a) {
    tr.clear();
    ss = fast_set(n);
    seg = segtree<Monoid>(a);
    rev.assign(n, false);
    rt.assign(n, -1);
    for (int pos = 0; pos < n; pos++) {
      assert(0 <= k[pos] && k[pos] < m0);
      ss.insert(pos);
      rt[pos] = make_node();
      set_key(rt[pos], 0, m0, k[pos], a[pos]);
    }
  }
};

} // namespace noya