Skip to content

sparse_segtree.hpp

SECTIONData Structure INCLUDEnoya/sparse_segtree.hpp

在巨大坐标域上动态开点,维护单点修改和区间聚合;适合无法按完整值域建树的在线问题。

Complexity: Time: O(log coordinate_range) per point update or range product. Space: O(u), where u is the number of distinct assigned positions.

AC 记录:point_set_range_composite_large_array

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(log coordinate_range) per point update or range
/// product. Space: O(u), where u is the number of distinct assigned positions.

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

namespace noya {

/// @brief Sparse segment tree for an enormous mostly-identity array.  Each
/// node stores one explicitly assigned position and the monoid product of its
/// binary-search-tree subtree; interval midpoints keep the height bounded by
/// the coordinate bit width while requiring only one node per touched point.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class Monoid, class Coordinate = std::int64_t> class sparse_segtree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    Coordinate pos;
    int ls = -1;
    int rs = -1;
    value_type val;
    value_type prd;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  int rt = -1;
  std::vector<node> tr;

  // Integer average rounded down without overflowing either endpoint.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  int new_node(Coordinate pos, const value_type &val) {
    tr.push_back({pos, -1, -1, val, val});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    value_type prd = tr[cur].val;
    if (tr[cur].ls != -1) {
      prd = Monoid::op(tr[tr[cur].ls].prd, prd);
    }
    if (tr[cur].rs != -1) {
      prd = Monoid::op(prd, tr[tr[cur].rs].prd);
    }
    tr[cur].prd = prd;
  }

  int set_rec(int cur, Coordinate low, Coordinate big, Coordinate pos,
              value_type val) {
    if (cur == -1) {
      return new_node(pos, val);
    }
    if (tr[cur].pos == pos) {
      tr[cur].val = val;
      pull(cur);
      return cur;
    }
    Coordinate mid = midpoint(low, big);
    if (pos < mid) {
      if (tr[cur].pos < pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].ls = set_rec(tr[cur].ls, low, mid, pos, std::move(val));
    } else {
      if (pos < tr[cur].pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].rs = set_rec(tr[cur].rs, mid, big, pos, std::move(val));
    }
    pull(cur);
    return cur;
  }

  void prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, value_type &res) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r || cur == -1) {
      return;
    }
    if (l == low && r == big) {
      res = Monoid::op(res, tr[cur].prd);
      return;
    }
    Coordinate mid = midpoint(low, big);
    prod_rec(tr[cur].ls, low, mid, l, r, res);
    if (l <= tr[cur].pos && tr[cur].pos < r) {
      res = Monoid::op(res, tr[cur].val);
    }
    prod_rec(tr[cur].rs, mid, big, l, r, res);
  }

public:
  sparse_segtree() = default;
  sparse_segtree(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
  }

  int stored_points() const { return int(tr.size()); }

  void set(Coordinate pos, const value_type &val) {
    assert(lo_ <= pos && pos < hi_);
    rt = set_rec(rt, lo_, hi_, pos, val);
  }

  value_type get(Coordinate pos) const {
    assert(lo_ <= pos && pos < hi_);
    int cur = rt;
    while (cur != -1) {
      if (pos == tr[cur].pos) {
        return tr[cur].val;
      }
      cur = pos < tr[cur].pos ? tr[cur].ls : tr[cur].rs;
    }
    return Monoid::unit();
  }

  value_type prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    value_type res = Monoid::unit();
    prod_rec(rt, lo_, hi_, l, r, res);
    return res;
  }

  value_type all_prod() const { return rt == -1 ? Monoid::unit() : tr[rt].prd; }
};

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

/// @complexity Time: O(log coordinate_range) per point update or range
/// product. Space: O(u), where u is the number of distinct assigned positions.

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

namespace noya {

/// @brief Sparse segment tree for an enormous mostly-identity array.  Each
/// node stores one explicitly assigned position and the monoid product of its
/// binary-search-tree subtree; interval midpoints keep the height bounded by
/// the coordinate bit width while requiring only one node per touched point.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class Monoid, class Coordinate = std::int64_t> class sparse_segtree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    Coordinate pos;
    int ls = -1;
    int rs = -1;
    value_type val;
    value_type prd;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  int rt = -1;
  std::vector<node> tr;

  // Integer average rounded down without overflowing either endpoint.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  int new_node(Coordinate pos, const value_type &val) {
    tr.push_back({pos, -1, -1, val, val});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    value_type prd = tr[cur].val;
    if (tr[cur].ls != -1) {
      prd = Monoid::op(tr[tr[cur].ls].prd, prd);
    }
    if (tr[cur].rs != -1) {
      prd = Monoid::op(prd, tr[tr[cur].rs].prd);
    }
    tr[cur].prd = prd;
  }

  int set_rec(int cur, Coordinate low, Coordinate big, Coordinate pos,
              value_type val) {
    if (cur == -1) {
      return new_node(pos, val);
    }
    if (tr[cur].pos == pos) {
      tr[cur].val = val;
      pull(cur);
      return cur;
    }
    Coordinate mid = midpoint(low, big);
    if (pos < mid) {
      if (tr[cur].pos < pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].ls = set_rec(tr[cur].ls, low, mid, pos, std::move(val));
    } else {
      if (pos < tr[cur].pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].rs = set_rec(tr[cur].rs, mid, big, pos, std::move(val));
    }
    pull(cur);
    return cur;
  }

  void prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, value_type &res) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r || cur == -1) {
      return;
    }
    if (l == low && r == big) {
      res = Monoid::op(res, tr[cur].prd);
      return;
    }
    Coordinate mid = midpoint(low, big);
    prod_rec(tr[cur].ls, low, mid, l, r, res);
    if (l <= tr[cur].pos && tr[cur].pos < r) {
      res = Monoid::op(res, tr[cur].val);
    }
    prod_rec(tr[cur].rs, mid, big, l, r, res);
  }

public:
  sparse_segtree() = default;
  sparse_segtree(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
  }

  int stored_points() const { return int(tr.size()); }

  void set(Coordinate pos, const value_type &val) {
    assert(lo_ <= pos && pos < hi_);
    rt = set_rec(rt, lo_, hi_, pos, val);
  }

  value_type get(Coordinate pos) const {
    assert(lo_ <= pos && pos < hi_);
    int cur = rt;
    while (cur != -1) {
      if (pos == tr[cur].pos) {
        return tr[cur].val;
      }
      cur = pos < tr[cur].pos ? tr[cur].ls : tr[cur].rs;
    }
    return Monoid::unit();
  }

  value_type prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    value_type res = Monoid::unit();
    prod_rec(rt, lo_, hi_, l, r, res);
    return res;
  }

  value_type all_prod() const { return rt == -1 ? Monoid::unit() : tr[rt].prd; }
};

} // namespace noya

#endif // NOYA_SPARSE_SEGTREE_HPP
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>

/// @complexity Time: O(log coordinate_range) per point update or range
/// product. Space: O(u), where u is the number of distinct assigned positions.

namespace noya {

/// @brief Sparse segment tree for an enormous mostly-identity array.  Each
/// node stores one explicitly assigned position and the monoid product of its
/// binary-search-tree subtree; interval midpoints keep the height bounded by
/// the coordinate bit width while requiring only one node per touched point.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class Monoid, class Coordinate = std::int64_t> class sparse_segtree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    Coordinate pos;
    int ls = -1;
    int rs = -1;
    value_type val;
    value_type prd;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  int rt = -1;
  std::vector<node> tr;

  // Integer average rounded down without overflowing either endpoint.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  int new_node(Coordinate pos, const value_type &val) {
    tr.push_back({pos, -1, -1, val, val});
    return int(tr.size()) - 1;
  }

  void pull(int cur) {
    value_type prd = tr[cur].val;
    if (tr[cur].ls != -1) {
      prd = Monoid::op(tr[tr[cur].ls].prd, prd);
    }
    if (tr[cur].rs != -1) {
      prd = Monoid::op(prd, tr[tr[cur].rs].prd);
    }
    tr[cur].prd = prd;
  }

  int set_rec(int cur, Coordinate low, Coordinate big, Coordinate pos,
              value_type val) {
    if (cur == -1) {
      return new_node(pos, val);
    }
    if (tr[cur].pos == pos) {
      tr[cur].val = val;
      pull(cur);
      return cur;
    }
    Coordinate mid = midpoint(low, big);
    if (pos < mid) {
      if (tr[cur].pos < pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].ls = set_rec(tr[cur].ls, low, mid, pos, std::move(val));
    } else {
      if (pos < tr[cur].pos) {
        std::swap(tr[cur].pos, pos);
        std::swap(tr[cur].val, val);
      }
      tr[cur].rs = set_rec(tr[cur].rs, mid, big, pos, std::move(val));
    }
    pull(cur);
    return cur;
  }

  void prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, value_type &res) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r || cur == -1) {
      return;
    }
    if (l == low && r == big) {
      res = Monoid::op(res, tr[cur].prd);
      return;
    }
    Coordinate mid = midpoint(low, big);
    prod_rec(tr[cur].ls, low, mid, l, r, res);
    if (l <= tr[cur].pos && tr[cur].pos < r) {
      res = Monoid::op(res, tr[cur].val);
    }
    prod_rec(tr[cur].rs, mid, big, l, r, res);
  }

public:
  sparse_segtree() = default;
  sparse_segtree(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
  }

  int stored_points() const { return int(tr.size()); }

  void set(Coordinate pos, const value_type &val) {
    assert(lo_ <= pos && pos < hi_);
    rt = set_rec(rt, lo_, hi_, pos, val);
  }

  value_type get(Coordinate pos) const {
    assert(lo_ <= pos && pos < hi_);
    int cur = rt;
    while (cur != -1) {
      if (pos == tr[cur].pos) {
        return tr[cur].val;
      }
      cur = pos < tr[cur].pos ? tr[cur].ls : tr[cur].rs;
    }
    return Monoid::unit();
  }

  value_type prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    value_type res = Monoid::unit();
    prod_rec(rt, lo_, hi_, l, r, res);
    return res;
  }

  value_type all_prod() const { return rt == -1 ? Monoid::unit() : tr[rt].prd; }
};

} // namespace noya