Skip to content

sparse_segtree.hpp

SECTIONData Structure INCLUDEnoya/sparse_segtree.hpp

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.

Verified by point_set_range_composite_large_array.

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

Implementation

View on GitHub

#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(number of distinct updated 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.
template <class Monoid, class Coordinate = std::int64_t>
class sparse_segtree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    Coordinate position;
    int left = -1;
    int right = -1;
    value_type value;
    value_type product;
  };

  Coordinate left_bound_ = 0;
  Coordinate right_bound_ = 0;
  int root_ = -1;
  std::vector<node> nodes_;

  int new_node(Coordinate position, const value_type &value) {
    nodes_.push_back({position, -1, -1, value, value});
    return int(nodes_.size()) - 1;
  }

  void pull(int current) {
    value_type product = nodes_[current].value;
    if (nodes_[current].left != -1) {
      product = Monoid::op(nodes_[nodes_[current].left].product, product);
    }
    if (nodes_[current].right != -1) {
      product = Monoid::op(product, nodes_[nodes_[current].right].product);
    }
    nodes_[current].product = product;
  }

  int set_rec(int current, Coordinate low, Coordinate high,
              Coordinate position, value_type value) {
    if (current == -1) {
      return new_node(position, value);
    }
    if (nodes_[current].position == position) {
      nodes_[current].value = value;
      pull(current);
      return current;
    }
    Coordinate middle = low + (high - low) / 2;
    if (position < middle) {
      if (nodes_[current].position < position) {
        std::swap(nodes_[current].position, position);
        std::swap(nodes_[current].value, value);
      }
      nodes_[current].left = set_rec(nodes_[current].left, low, middle,
                                     position, std::move(value));
    } else {
      if (position < nodes_[current].position) {
        std::swap(nodes_[current].position, position);
        std::swap(nodes_[current].value, value);
      }
      nodes_[current].right = set_rec(nodes_[current].right, middle, high,
                                      position, std::move(value));
    }
    pull(current);
    return current;
  }

  void prod_rec(int current, Coordinate low, Coordinate high, Coordinate left,
                Coordinate right, value_type &result) const {
    left = std::max(left, low);
    right = std::min(right, high);
    if (left >= right || current == -1) {
      return;
    }
    if (left == low && right == high) {
      result = Monoid::op(result, nodes_[current].product);
      return;
    }
    Coordinate middle = low + (high - low) / 2;
    prod_rec(nodes_[current].left, low, middle, left, right, result);
    if (left <= nodes_[current].position &&
        nodes_[current].position < right) {
      result = Monoid::op(result, nodes_[current].value);
    }
    prod_rec(nodes_[current].right, middle, high, left, right, result);
  }

public:
  sparse_segtree() = default;
  sparse_segtree(Coordinate left_bound, Coordinate right_bound,
                 std::size_t maximum_points = 0)
      : left_bound_(left_bound), right_bound_(right_bound) {
    assert(left_bound_ < right_bound_);
    nodes_.reserve(maximum_points);
  }

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

  void set(Coordinate position, const value_type &value) {
    assert(left_bound_ <= position && position < right_bound_);
    root_ = set_rec(root_, left_bound_, right_bound_, position, value);
  }

  value_type get(Coordinate position) const {
    assert(left_bound_ <= position && position < right_bound_);
    int current = root_;
    while (current != -1) {
      if (position == nodes_[current].position) {
        return nodes_[current].value;
      }
      current = position < nodes_[current].position
                    ? nodes_[current].left
                    : nodes_[current].right;
    }
    return Monoid::unit();
  }

  value_type prod(Coordinate left, Coordinate right) const {
    assert(left_bound_ <= left && left <= right && right <= right_bound_);
    value_type result = Monoid::unit();
    prod_rec(root_, left_bound_, right_bound_, left, right, result);
    return result;
  }

  value_type all_prod() const {
    return root_ == -1 ? Monoid::unit() : nodes_[root_].product;
  }
};

} // 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(number of distinct updated 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.
template <class Monoid, class Coordinate = std::int64_t>
class sparse_segtree {
public:
  using value_type = typename Monoid::value_type;

private:
  struct node {
    Coordinate position;
    int left = -1;
    int right = -1;
    value_type value;
    value_type product;
  };

  Coordinate left_bound_ = 0;
  Coordinate right_bound_ = 0;
  int root_ = -1;
  std::vector<node> nodes_;

  int new_node(Coordinate position, const value_type &value) {
    nodes_.push_back({position, -1, -1, value, value});
    return int(nodes_.size()) - 1;
  }

  void pull(int current) {
    value_type product = nodes_[current].value;
    if (nodes_[current].left != -1) {
      product = Monoid::op(nodes_[nodes_[current].left].product, product);
    }
    if (nodes_[current].right != -1) {
      product = Monoid::op(product, nodes_[nodes_[current].right].product);
    }
    nodes_[current].product = product;
  }

  int set_rec(int current, Coordinate low, Coordinate high,
              Coordinate position, value_type value) {
    if (current == -1) {
      return new_node(position, value);
    }
    if (nodes_[current].position == position) {
      nodes_[current].value = value;
      pull(current);
      return current;
    }
    Coordinate middle = low + (high - low) / 2;
    if (position < middle) {
      if (nodes_[current].position < position) {
        std::swap(nodes_[current].position, position);
        std::swap(nodes_[current].value, value);
      }
      nodes_[current].left = set_rec(nodes_[current].left, low, middle,
                                     position, std::move(value));
    } else {
      if (position < nodes_[current].position) {
        std::swap(nodes_[current].position, position);
        std::swap(nodes_[current].value, value);
      }
      nodes_[current].right = set_rec(nodes_[current].right, middle, high,
                                      position, std::move(value));
    }
    pull(current);
    return current;
  }

  void prod_rec(int current, Coordinate low, Coordinate high, Coordinate left,
                Coordinate right, value_type &result) const {
    left = std::max(left, low);
    right = std::min(right, high);
    if (left >= right || current == -1) {
      return;
    }
    if (left == low && right == high) {
      result = Monoid::op(result, nodes_[current].product);
      return;
    }
    Coordinate middle = low + (high - low) / 2;
    prod_rec(nodes_[current].left, low, middle, left, right, result);
    if (left <= nodes_[current].position &&
        nodes_[current].position < right) {
      result = Monoid::op(result, nodes_[current].value);
    }
    prod_rec(nodes_[current].right, middle, high, left, right, result);
  }

public:
  sparse_segtree() = default;
  sparse_segtree(Coordinate left_bound, Coordinate right_bound,
                 std::size_t maximum_points = 0)
      : left_bound_(left_bound), right_bound_(right_bound) {
    assert(left_bound_ < right_bound_);
    nodes_.reserve(maximum_points);
  }

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

  void set(Coordinate position, const value_type &value) {
    assert(left_bound_ <= position && position < right_bound_);
    root_ = set_rec(root_, left_bound_, right_bound_, position, value);
  }

  value_type get(Coordinate position) const {
    assert(left_bound_ <= position && position < right_bound_);
    int current = root_;
    while (current != -1) {
      if (position == nodes_[current].position) {
        return nodes_[current].value;
      }
      current = position < nodes_[current].position
                    ? nodes_[current].left
                    : nodes_[current].right;
    }
    return Monoid::unit();
  }

  value_type prod(Coordinate left, Coordinate right) const {
    assert(left_bound_ <= left && left <= right && right <= right_bound_);
    value_type result = Monoid::unit();
    prod_rec(root_, left_bound_, right_bound_, left, right, result);
    return result;
  }

  value_type all_prod() const {
    return root_ == -1 ? Monoid::unit() : nodes_[root_].product;
  }
};

} // namespace noya