Persistent Dynamic Dual Segment Tree
(ds/segtree/persistent_dynamic_dual_segtree.hpp)
- View this file on GitHub
- Last update: 2026-08-12 03:11:00+09:00
- Include:
#include "ds/segtree/persistent_dynamic_dual_segtree.hpp"
Overview
m1une::ds::PersistentDynamicDualSegtree is a persistent sparse dual segment
tree for range monoid updates and point queries. Each assignment or update
returns a new version while preserving every earlier version.
apply(l, r, x) changes each point value v in [l, r) to
Monoid::op(x, v). This order is preserved for non-commutative monoids.
Untouched coordinates have one uniform initial_value.
Versions derived from the same root share a contiguous node pool. Queries do
not push tags, mutate versions, or allocate nodes. Unreferenced nodes are
recycled after version destruction, assignment, or release().
The _inplace updates mutate only the current handle with copy-on-write,
cloning shared nodes before a write and reusing unique nodes. Ordinary set and
apply continue to return new persistent versions.
Template Parameters
-
Monoid: A type satisfyingm1une::monoid::IsMonoid. -
Index: A non-boolintegral coordinate type. The default islong long.
Construction
-
PersistentDynamicDualSegtree(): creates an empty domain[0, 0). -
PersistentDynamicDualSegtree(Index n): creates[0, n)with identity values. -
PersistentDynamicDualSegtree(Index left, Index right): creates[left, right)with identity values. -
PersistentDynamicDualSegtree(Index left, Index right, T initial_value): creates a domain with the specified uniform initial point value.
Construction takes $O(1)$ time and storage.
Methods
Let $U$ be the domain length and $K$ the number of live nodes in the shared version family.
| Method | Description | Complexity |
|---|---|---|
size_type size() |
Returns the unsigned domain length. | $O(1)$ |
bool empty() |
Returns whether the domain is empty. | $O(1)$ |
Index left_bound() |
Returns the left endpoint. | $O(1)$ |
Index right_bound() |
Returns the right endpoint. | $O(1)$ |
const T& initial_value() |
Returns the uniform initial point value. | $O(1)$ |
void reserve(size_t n) |
Reserves shared-pool space for n nodes. |
$O(K)$ |
size_t node_count() |
Returns live nodes across shared versions. | $O(1)$ |
void release() |
Releases this root and resets the handle to the uniform initial version. | $O(F)$ |
PersistentDynamicDualSegtree set(Index p, T x) |
Returns a version assigning x at p. |
$O(\log U)$ |
void set_inplace(Index p, T x) |
Assigns x in this version using copy-on-write. |
$O(\log U)$ |
T get(Index p) |
Returns the current value at p. |
$O(\log U)$ |
T operator[](Index p) |
Equivalent to get(p). |
$O(\log U)$ |
PersistentDynamicDualSegtree apply(Index p, T x) |
Returns a version applying x at p. |
$O(\log U)$ |
PersistentDynamicDualSegtree apply(Index l, Index r, T x) |
Returns a version applying x over [l, r). |
$O(\log U)$ |
void apply_inplace(Index p, const T& x) |
Applies x at p in this version using copy-on-write. |
$O(\log U)$ |
void apply_inplace(Index l, Index r, const T& x) |
Applies x over [l, r) in this version using copy-on-write. |
$O(\log U)$ |
Each new update allocates $O(\log U)$ nodes in the worst case. Copying a version is $O(1)$. Here $F$ is the number of nodes freed by a release. Released slots are reused by later updates.
Example
#include "ds/segtree/persistent_dynamic_dual_segtree.hpp"
#include "monoid/add.hpp"
#include <iostream>
int main() {
using Add = m1une::monoid::Add<long long>;
using Seg = m1une::ds::PersistentDynamicDualSegtree<Add>;
Seg base(-1'000'000'000LL, 1'000'000'001LL, 0);
Seg first = base.apply(-100, 200, 7);
Seg second = first.apply(50, 60, 3);
std::cout << base.get(55) << "\n"; // 0
std::cout << first.get(55) << "\n"; // 7
std::cout << second.get(55) << "\n"; // 10
}
Depends on
ds/segtree/dynamic_segtree_common.hpp
ds/segtree/persistent_node_pool.hpp
Monoid Concept
(monoid/concept.hpp)
Verified with
Code
#ifndef M1UNE_PERSISTENT_DYNAMIC_DUAL_SEGTREE_HPP
#define M1UNE_PERSISTENT_DYNAMIC_DUAL_SEGTREE_HPP 1
#include <cassert>
#include <concepts>
#include <cstddef>
#include <limits>
#include <memory>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#include "../../monoid/concept.hpp"
#include "dynamic_segtree_common.hpp"
#include "persistent_node_pool.hpp"
namespace m1une {
namespace ds {
// A persistent sparse dual segment tree over an integral half-open interval.
template <m1une::monoid::IsMonoid Monoid, std::integral Index = long long>
requires(!std::same_as<std::remove_cv_t<Index>, bool>)
struct PersistentDynamicDualSegtree {
using T = typename Monoid::value_type;
using index_type = Index;
using size_type = detail::dynamic_size_type<Index>;
private:
struct Node {
T val;
int left;
int right;
int references;
bool has_lazy;
Node() : val(Monoid::id()), left(0), right(0), references(0), has_lazy(false) {}
};
struct Config {
Index left;
Index right;
T initial_value;
Config(Index left_bound, Index right_bound, T value)
: left(left_bound), right(right_bound), initial_value(std::move(value)) {
assert(left <= right);
}
};
std::shared_ptr<const Config> _config;
using Pool = detail::PersistentNodePool<Node>;
std::shared_ptr<Pool> _pool;
int _root;
PersistentDynamicDualSegtree(std::shared_ptr<const Config> config, std::shared_ptr<Pool> pool, int root)
: _config(std::move(config)), _pool(std::move(pool)), _root(root) {
_pool->retain(_root);
}
int new_node() const { return _pool->emplace(); }
int clone_or_new(int t, bool copy_on_write = false) const {
if (!t) return new_node();
return copy_on_write ? _pool->clone_if_shared(t) : _pool->clone(t);
}
void all_apply_to_node(int t, Index left, Index right, const T& x) const {
Node& node = (*_pool)[t];
if (std::midpoint(left, right) == left) {
T value = node.has_lazy ? node.val : _config->initial_value;
node.val = Monoid::op(x, value);
node.has_lazy = true;
} else {
node.val = node.has_lazy ? Monoid::op(x, node.val) : x;
node.has_lazy = true;
}
}
int all_apply_clone(int t, Index left, Index right, const T& x, bool copy_on_write = false) const {
int result = clone_or_new(t, copy_on_write);
all_apply_to_node(result, left, right, x);
return result;
}
void push(int t, Index left, Index right, bool copy_on_write = false) const {
if (!(*_pool)[t].has_lazy) return;
Index middle = std::midpoint(left, right);
if (middle == left) return;
T lazy = (*_pool)[t].val;
int left_child = all_apply_clone((*_pool)[t].left, left, middle, lazy, copy_on_write);
int right_child = all_apply_clone((*_pool)[t].right, middle, right, lazy, copy_on_write);
Node& node = (*_pool)[t];
_pool->replace(node.left, left_child);
_pool->replace(node.right, right_child);
node.val = Monoid::id();
node.has_lazy = false;
}
int set_node(int t, Index left, Index right, Index p, T x, bool copy_on_write = false) const {
t = clone_or_new(t, copy_on_write);
Index middle = std::midpoint(left, right);
if (middle == left) {
Node& node = (*_pool)[t];
node.val = std::move(x);
node.has_lazy = true;
return t;
}
push(t, left, right, copy_on_write);
if (p < middle) {
int child = set_node((*_pool)[t].left, left, middle, p, std::move(x), copy_on_write);
_pool->replace((*_pool)[t].left, child);
} else {
int child = set_node((*_pool)[t].right, middle, right, p, std::move(x), copy_on_write);
_pool->replace((*_pool)[t].right, child);
}
return t;
}
int apply_node(int t, Index left, Index right, Index query_left, Index query_right, const T& x,
bool copy_on_write = false) const {
if (query_right <= left || right <= query_left) return t;
if (query_left <= left && right <= query_right) {
return all_apply_clone(t, left, right, x, copy_on_write);
}
t = clone_or_new(t, copy_on_write);
push(t, left, right, copy_on_write);
Index middle = std::midpoint(left, right);
int left_child = apply_node((*_pool)[t].left, left, middle, query_left, query_right, x, copy_on_write);
int right_child = apply_node((*_pool)[t].right, middle, right, query_left, query_right, x, copy_on_write);
_pool->replace((*_pool)[t].left, left_child);
_pool->replace((*_pool)[t].right, right_child);
return t;
}
T compose(const T& inherited, int t) const {
if (!t || !(*_pool)[t].has_lazy) return inherited;
return Monoid::op(inherited, (*_pool)[t].val);
}
public:
PersistentDynamicDualSegtree() : PersistentDynamicDualSegtree(Index(0), Index(0), Monoid::id()) {}
explicit PersistentDynamicDualSegtree(Index n) : PersistentDynamicDualSegtree(Index(0), n, Monoid::id()) {
if constexpr (std::signed_integral<Index>) assert(Index(0) <= n);
}
PersistentDynamicDualSegtree(Index left, Index right) : PersistentDynamicDualSegtree(left, right, Monoid::id()) {}
PersistentDynamicDualSegtree(Index left, Index right, T initial_value)
: _config(std::make_shared<Config>(left, right, std::move(initial_value))),
_pool(std::make_shared<Pool>()),
_root(0) {}
PersistentDynamicDualSegtree(const PersistentDynamicDualSegtree& other)
: _config(other._config), _pool(other._pool), _root(other._root) {
if (_pool) _pool->retain(_root);
}
PersistentDynamicDualSegtree(PersistentDynamicDualSegtree&& other) noexcept
: _config(std::move(other._config)), _pool(std::move(other._pool)), _root(other._root) {
other._root = 0;
}
PersistentDynamicDualSegtree& operator=(const PersistentDynamicDualSegtree& other) {
if (this == &other) return *this;
if (other._pool) other._pool->retain(other._root);
if (_pool) _pool->release(_root);
_config = other._config;
_pool = other._pool;
_root = other._root;
return *this;
}
PersistentDynamicDualSegtree& operator=(PersistentDynamicDualSegtree&& other) noexcept {
if (this == &other) return *this;
if (_pool) _pool->release(_root);
_config = std::move(other._config);
_pool = std::move(other._pool);
_root = other._root;
other._root = 0;
return *this;
}
~PersistentDynamicDualSegtree() {
if (_pool) _pool->release(_root);
}
size_type size() const { return detail::dynamic_distance(_config->left, _config->right); }
bool empty() const { return _config->left == _config->right; }
Index left_bound() const { return _config->left; }
Index right_bound() const { return _config->right; }
const T& initial_value() const { return _config->initial_value; }
void reserve(std::size_t node_capacity) const {
assert(node_capacity < std::numeric_limits<std::size_t>::max());
_pool->reserve(node_capacity);
}
std::size_t node_count() const { return _pool->size(); }
void release() {
if (_pool) _pool->release(_root);
_pool = std::make_shared<Pool>();
_root = 0;
}
PersistentDynamicDualSegtree set(Index p, T x) const {
assert(left_bound() <= p && p < right_bound());
return PersistentDynamicDualSegtree(_config, _pool,
set_node(_root, left_bound(), right_bound(), p, std::move(x)));
}
void set_inplace(Index p, T x) {
assert(left_bound() <= p && p < right_bound());
int root = set_node(_root, left_bound(), right_bound(), p, std::move(x), true);
_pool->replace(_root, root);
}
T get(Index p) const {
assert(left_bound() <= p && p < right_bound());
int t = _root;
Index left = left_bound();
Index right = right_bound();
T inherited = Monoid::id();
while (t) {
Index middle = std::midpoint(left, right);
if (middle == left) {
T value = (*_pool)[t].has_lazy ? (*_pool)[t].val : initial_value();
return Monoid::op(inherited, value);
}
inherited = compose(inherited, t);
if (p < middle) {
t = (*_pool)[t].left;
right = middle;
} else {
t = (*_pool)[t].right;
left = middle;
}
}
return Monoid::op(inherited, initial_value());
}
T operator[](Index p) const { return get(p); }
PersistentDynamicDualSegtree apply(Index p, const T& x) const {
assert(left_bound() <= p && p < right_bound());
return apply(p, p + 1, x);
}
PersistentDynamicDualSegtree apply(Index left, Index right, const T& x) const {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return *this;
return PersistentDynamicDualSegtree(_config, _pool,
apply_node(_root, left_bound(), right_bound(), left, right, x));
}
void apply_inplace(Index p, const T& x) {
assert(left_bound() <= p && p < right_bound());
apply_inplace(p, p + 1, x);
}
void apply_inplace(Index left, Index right, const T& x) {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return;
int root = apply_node(_root, left_bound(), right_bound(), left, right, x, true);
_pool->replace(_root, root);
}
};
} // namespace ds
} // namespace m1une
#endif // M1UNE_PERSISTENT_DYNAMIC_DUAL_SEGTREE_HPP#line 1 "ds/segtree/persistent_dynamic_dual_segtree.hpp"
#include <cassert>
#include <concepts>
#include <cstddef>
#include <limits>
#include <memory>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#line 1 "monoid/concept.hpp"
#line 5 "monoid/concept.hpp"
namespace m1une {
namespace monoid {
// Concept to check if a type satisfies the requirements of a Monoid.
// A Monoid must have a `value_type`, an identity element `id()`, and an associative binary operation `op()`.
template <typename M>
concept IsMonoid = requires(typename M::value_type a, typename M::value_type b) {
// 1. Must define `value_type`
typename M::value_type;
// 2. Must have a static method `id()` returning `value_type`
{ M::id() } -> std::same_as<typename M::value_type>;
// 3. Must have a static method `op(a, b)` returning `value_type`
{ M::op(a, b) } -> std::same_as<typename M::value_type>;
};
// Concept for groups. A type satisfying this concept must also obey the group
// laws; concepts can check the interface but not the algebraic properties.
template <typename M>
concept IsGroup = IsMonoid<M> && requires(typename M::value_type a) {
{ M::inv(a) } -> std::same_as<typename M::value_type>;
};
// Concept for commutative groups. Commutativity is a semantic requirement and
// cannot be checked by a C++ concept.
template <typename M>
concept IsCommutativeGroup = IsGroup<M>;
} // namespace monoid
} // namespace m1une
#line 1 "ds/segtree/dynamic_segtree_common.hpp"
#line 11 "ds/segtree/dynamic_segtree_common.hpp"
namespace m1une {
namespace ds {
namespace detail {
template <std::integral Index>
using dynamic_size_type = std::make_unsigned_t<Index>;
template <std::integral Index>
constexpr dynamic_size_type<Index> dynamic_distance(Index left, Index right) {
return static_cast<dynamic_size_type<Index>>(right) - static_cast<dynamic_size_type<Index>>(left);
}
template <class Monoid, class Size>
typename Monoid::value_type monoid_repeat(typename Monoid::value_type value, Size count) {
typename Monoid::value_type result = Monoid::id();
while (count != 0) {
if (count & 1) result = Monoid::op(result, value);
count >>= 1;
if (count != 0) value = Monoid::op(value, value);
}
return result;
}
template <class ActedMonoid>
typename ActedMonoid::value_type dynamic_mapping(
const typename ActedMonoid::operator_type& f,
const typename ActedMonoid::value_type& value
) {
using F = typename ActedMonoid::operator_type;
using T = typename ActedMonoid::value_type;
if constexpr (requires(F g, T x, long long ord) { ActedMonoid::mapping(g, x, ord); }) {
return ActedMonoid::mapping(f, value, 0);
} else {
return ActedMonoid::mapping(f, value);
}
}
template <class ActedMonoid, class Size>
typename ActedMonoid::operator_type dynamic_shift(
const typename ActedMonoid::operator_type& f,
Size offset
) {
using F = typename ActedMonoid::operator_type;
if constexpr (requires(F g, long long ord) { ActedMonoid::op_shift(g, ord); }) {
assert(offset <= static_cast<Size>(std::numeric_limits<long long>::max()));
return ActedMonoid::op_shift(f, static_cast<long long>(offset));
} else {
return f;
}
}
template <class Monoid, std::integral Index>
class UniformMonoidDomain {
public:
using T = typename Monoid::value_type;
using size_type = dynamic_size_type<Index>;
private:
struct Level {
size_type small_length;
T small_value;
T large_value;
};
Index _left;
Index _right;
T _initial_value;
std::vector<Level> _levels;
public:
UniformMonoidDomain(Index left, Index right, T initial_value)
: _left(left), _right(right), _initial_value(std::move(initial_value)) {
assert(left <= right);
size_type n = size();
constexpr int digits = std::numeric_limits<size_type>::digits;
_levels.reserve(digits + 1);
for (int depth = 0; depth <= digits; depth++) {
size_type small = depth == digits ? 0 : n >> depth;
size_type large = small;
if (depth != 0) {
bool has_remainder;
if (depth == digits) {
has_remainder = n != 0;
} else {
size_type mask = (size_type(1) << depth) - 1;
has_remainder = (n & mask) != 0;
}
if (has_remainder) large++;
}
_levels.push_back(Level{
small,
monoid_repeat<Monoid>(_initial_value, small),
monoid_repeat<Monoid>(_initial_value, large),
});
}
}
Index left_bound() const {
return _left;
}
Index right_bound() const {
return _right;
}
size_type size() const {
return dynamic_distance(_left, _right);
}
bool empty() const {
return _left == _right;
}
const T& initial_value() const {
return _initial_value;
}
const T& default_product(int depth, Index left, Index right) const {
assert(0 <= depth && depth < int(_levels.size()));
const Level& level = _levels[depth];
size_type length = dynamic_distance(left, right);
if (length == level.small_length) return level.small_value;
assert(length == level.small_length + 1);
return level.large_value;
}
};
} // namespace detail
} // namespace ds
} // namespace m1une
#line 1 "ds/segtree/persistent_node_pool.hpp"
#line 9 "ds/segtree/persistent_node_pool.hpp"
namespace m1une {
namespace ds {
namespace detail {
// Node must have integer `left`, `right`, and `references` members.
template <class Node>
struct PersistentNodePool {
std::vector<Node> nodes;
int first_free = 0;
std::size_t live_nodes = 0;
private:
void release_zero(int node) {
int left = nodes[node].left;
int right = nodes[node].right;
nodes[node] = Node();
nodes[node].left = first_free;
first_free = node;
--live_nodes;
if (left && --nodes[left].references == 0) release_zero(left);
if (right && --nodes[right].references == 0) release_zero(right);
}
public:
PersistentNodePool() { nodes.emplace_back(); }
void reserve(std::size_t capacity) { nodes.reserve(capacity + 1); }
Node& operator[](int node) { return nodes[node]; }
const Node& operator[](int node) const { return nodes[node]; }
void retain(int node) {
if (node) ++nodes[node].references;
}
void release(int node) {
if (!node) return;
assert(nodes[node].references > 0);
if (--nodes[node].references == 0) release_zero(node);
}
template <class... Args>
int emplace(Args&&... args) {
int result;
if (!first_free) {
assert(nodes.size() < std::size_t(std::numeric_limits<int>::max()));
nodes.emplace_back(std::forward<Args>(args)...);
result = int(nodes.size()) - 1;
} else {
result = first_free;
first_free = nodes[result].left;
nodes[result] = Node(std::forward<Args>(args)...);
}
Node& node = nodes[result];
node.references = 0;
retain(node.left);
retain(node.right);
++live_nodes;
return result;
}
int clone(int node) {
assert(node);
Node copy = nodes[node];
return emplace(std::move(copy));
}
bool unique(int node) const {
return !node || nodes[node].references == 1;
}
// Returns node itself when it has one owner, otherwise an unowned clone.
// The caller must attach a returned clone with replace() before it can be
// released or exposed as a root.
int clone_if_shared(int node) {
if (unique(node)) return node;
return clone(node);
}
void replace(int& edge, int node) {
if (edge == node) return;
retain(node);
int old = edge;
edge = node;
release(old);
}
std::size_t size() const { return live_nodes; }
};
} // namespace detail
} // namespace ds
} // namespace m1une
#line 17 "ds/segtree/persistent_dynamic_dual_segtree.hpp"
namespace m1une {
namespace ds {
// A persistent sparse dual segment tree over an integral half-open interval.
template <m1une::monoid::IsMonoid Monoid, std::integral Index = long long>
requires(!std::same_as<std::remove_cv_t<Index>, bool>)
struct PersistentDynamicDualSegtree {
using T = typename Monoid::value_type;
using index_type = Index;
using size_type = detail::dynamic_size_type<Index>;
private:
struct Node {
T val;
int left;
int right;
int references;
bool has_lazy;
Node() : val(Monoid::id()), left(0), right(0), references(0), has_lazy(false) {}
};
struct Config {
Index left;
Index right;
T initial_value;
Config(Index left_bound, Index right_bound, T value)
: left(left_bound), right(right_bound), initial_value(std::move(value)) {
assert(left <= right);
}
};
std::shared_ptr<const Config> _config;
using Pool = detail::PersistentNodePool<Node>;
std::shared_ptr<Pool> _pool;
int _root;
PersistentDynamicDualSegtree(std::shared_ptr<const Config> config, std::shared_ptr<Pool> pool, int root)
: _config(std::move(config)), _pool(std::move(pool)), _root(root) {
_pool->retain(_root);
}
int new_node() const { return _pool->emplace(); }
int clone_or_new(int t, bool copy_on_write = false) const {
if (!t) return new_node();
return copy_on_write ? _pool->clone_if_shared(t) : _pool->clone(t);
}
void all_apply_to_node(int t, Index left, Index right, const T& x) const {
Node& node = (*_pool)[t];
if (std::midpoint(left, right) == left) {
T value = node.has_lazy ? node.val : _config->initial_value;
node.val = Monoid::op(x, value);
node.has_lazy = true;
} else {
node.val = node.has_lazy ? Monoid::op(x, node.val) : x;
node.has_lazy = true;
}
}
int all_apply_clone(int t, Index left, Index right, const T& x, bool copy_on_write = false) const {
int result = clone_or_new(t, copy_on_write);
all_apply_to_node(result, left, right, x);
return result;
}
void push(int t, Index left, Index right, bool copy_on_write = false) const {
if (!(*_pool)[t].has_lazy) return;
Index middle = std::midpoint(left, right);
if (middle == left) return;
T lazy = (*_pool)[t].val;
int left_child = all_apply_clone((*_pool)[t].left, left, middle, lazy, copy_on_write);
int right_child = all_apply_clone((*_pool)[t].right, middle, right, lazy, copy_on_write);
Node& node = (*_pool)[t];
_pool->replace(node.left, left_child);
_pool->replace(node.right, right_child);
node.val = Monoid::id();
node.has_lazy = false;
}
int set_node(int t, Index left, Index right, Index p, T x, bool copy_on_write = false) const {
t = clone_or_new(t, copy_on_write);
Index middle = std::midpoint(left, right);
if (middle == left) {
Node& node = (*_pool)[t];
node.val = std::move(x);
node.has_lazy = true;
return t;
}
push(t, left, right, copy_on_write);
if (p < middle) {
int child = set_node((*_pool)[t].left, left, middle, p, std::move(x), copy_on_write);
_pool->replace((*_pool)[t].left, child);
} else {
int child = set_node((*_pool)[t].right, middle, right, p, std::move(x), copy_on_write);
_pool->replace((*_pool)[t].right, child);
}
return t;
}
int apply_node(int t, Index left, Index right, Index query_left, Index query_right, const T& x,
bool copy_on_write = false) const {
if (query_right <= left || right <= query_left) return t;
if (query_left <= left && right <= query_right) {
return all_apply_clone(t, left, right, x, copy_on_write);
}
t = clone_or_new(t, copy_on_write);
push(t, left, right, copy_on_write);
Index middle = std::midpoint(left, right);
int left_child = apply_node((*_pool)[t].left, left, middle, query_left, query_right, x, copy_on_write);
int right_child = apply_node((*_pool)[t].right, middle, right, query_left, query_right, x, copy_on_write);
_pool->replace((*_pool)[t].left, left_child);
_pool->replace((*_pool)[t].right, right_child);
return t;
}
T compose(const T& inherited, int t) const {
if (!t || !(*_pool)[t].has_lazy) return inherited;
return Monoid::op(inherited, (*_pool)[t].val);
}
public:
PersistentDynamicDualSegtree() : PersistentDynamicDualSegtree(Index(0), Index(0), Monoid::id()) {}
explicit PersistentDynamicDualSegtree(Index n) : PersistentDynamicDualSegtree(Index(0), n, Monoid::id()) {
if constexpr (std::signed_integral<Index>) assert(Index(0) <= n);
}
PersistentDynamicDualSegtree(Index left, Index right) : PersistentDynamicDualSegtree(left, right, Monoid::id()) {}
PersistentDynamicDualSegtree(Index left, Index right, T initial_value)
: _config(std::make_shared<Config>(left, right, std::move(initial_value))),
_pool(std::make_shared<Pool>()),
_root(0) {}
PersistentDynamicDualSegtree(const PersistentDynamicDualSegtree& other)
: _config(other._config), _pool(other._pool), _root(other._root) {
if (_pool) _pool->retain(_root);
}
PersistentDynamicDualSegtree(PersistentDynamicDualSegtree&& other) noexcept
: _config(std::move(other._config)), _pool(std::move(other._pool)), _root(other._root) {
other._root = 0;
}
PersistentDynamicDualSegtree& operator=(const PersistentDynamicDualSegtree& other) {
if (this == &other) return *this;
if (other._pool) other._pool->retain(other._root);
if (_pool) _pool->release(_root);
_config = other._config;
_pool = other._pool;
_root = other._root;
return *this;
}
PersistentDynamicDualSegtree& operator=(PersistentDynamicDualSegtree&& other) noexcept {
if (this == &other) return *this;
if (_pool) _pool->release(_root);
_config = std::move(other._config);
_pool = std::move(other._pool);
_root = other._root;
other._root = 0;
return *this;
}
~PersistentDynamicDualSegtree() {
if (_pool) _pool->release(_root);
}
size_type size() const { return detail::dynamic_distance(_config->left, _config->right); }
bool empty() const { return _config->left == _config->right; }
Index left_bound() const { return _config->left; }
Index right_bound() const { return _config->right; }
const T& initial_value() const { return _config->initial_value; }
void reserve(std::size_t node_capacity) const {
assert(node_capacity < std::numeric_limits<std::size_t>::max());
_pool->reserve(node_capacity);
}
std::size_t node_count() const { return _pool->size(); }
void release() {
if (_pool) _pool->release(_root);
_pool = std::make_shared<Pool>();
_root = 0;
}
PersistentDynamicDualSegtree set(Index p, T x) const {
assert(left_bound() <= p && p < right_bound());
return PersistentDynamicDualSegtree(_config, _pool,
set_node(_root, left_bound(), right_bound(), p, std::move(x)));
}
void set_inplace(Index p, T x) {
assert(left_bound() <= p && p < right_bound());
int root = set_node(_root, left_bound(), right_bound(), p, std::move(x), true);
_pool->replace(_root, root);
}
T get(Index p) const {
assert(left_bound() <= p && p < right_bound());
int t = _root;
Index left = left_bound();
Index right = right_bound();
T inherited = Monoid::id();
while (t) {
Index middle = std::midpoint(left, right);
if (middle == left) {
T value = (*_pool)[t].has_lazy ? (*_pool)[t].val : initial_value();
return Monoid::op(inherited, value);
}
inherited = compose(inherited, t);
if (p < middle) {
t = (*_pool)[t].left;
right = middle;
} else {
t = (*_pool)[t].right;
left = middle;
}
}
return Monoid::op(inherited, initial_value());
}
T operator[](Index p) const { return get(p); }
PersistentDynamicDualSegtree apply(Index p, const T& x) const {
assert(left_bound() <= p && p < right_bound());
return apply(p, p + 1, x);
}
PersistentDynamicDualSegtree apply(Index left, Index right, const T& x) const {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return *this;
return PersistentDynamicDualSegtree(_config, _pool,
apply_node(_root, left_bound(), right_bound(), left, right, x));
}
void apply_inplace(Index p, const T& x) {
assert(left_bound() <= p && p < right_bound());
apply_inplace(p, p + 1, x);
}
void apply_inplace(Index left, Index right, const T& x) {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return;
int root = apply_node(_root, left_bound(), right_bound(), left, right, x, true);
_pool->replace(_root, root);
}
};
} // namespace ds
} // namespace m1une