Dynamic Lazy Segment Tree
(ds/segtree/dynamic_lazy_segtree.hpp)
- View this file on GitHub
- Last update: 2026-06-21 04:34:53+09:00
- Include:
#include "ds/segtree/dynamic_lazy_segtree.hpp"
Overview
m1une::ds::DynamicLazySegtree is a sparse lazy segment tree over a fixed
integer coordinate domain. It supports point assignment, range updates, range
products, and boundary searches without allocating a dense array for the whole
domain.
Nodes use one contiguous pool and are created only for segments touched by an update or assignment. Read-only operations do not push lazy tags or allocate nodes.
Untouched Coordinates
Every coordinate starts with the same initial_value. The tree computes the
monoid product of untouched segments from that leaf value. This detail matters
for size-aware acted monoids:
using AM = m1une::acted_monoid::RangeAddRangeSum<long long>;
m1une::ds::DynamicLazySegtree<AM> seg(0, 1'000'000'000LL, AM::make(0));
AM::make(0) represents one zero-valued element. In contrast, AM::id() has
size zero and represents an empty product, so range additions would not affect
untouched coordinates.
Products preserve coordinate order, so non-commutative value monoids are supported when the acted-monoid laws hold.
Template Parameters
-
ActedMonoid: A type satisfyingm1une::acted_monoid::IsActedMonoid. -
Index: A non-boolintegral coordinate type. The default islong long.
Position-dependent operators may additionally provide op_shift(f, offset),
with the same semantics used by LazySegtree. Existing shifted acted monoids
use a long long offset, so every applied interval length must fit in
long long when op_shift is present.
Construction
-
DynamicLazySegtree(): creates an empty domain[0, 0). -
DynamicLazySegtree(Index n): creates[0, n)with initial leafActedMonoid::id(). -
DynamicLazySegtree(Index left, Index right): creates[left, right)with initial leafActedMonoid::id(). -
DynamicLazySegtree(Index left, Index right, T initial_value): creates a domain with the specified uniform initial leaf.
Construction precomputes untouched products for the possible segment lengths at each depth. It uses $O(\log U)$ memory and $O(\log^2 U)$ monoid operations, where $U$ is the number of coordinates.
Methods
| Method | Description | Complexity |
|---|---|---|
size_type size() |
Returns the unsigned domain length. | $O(1)$ |
bool empty() |
Returns whether the coordinate domain is empty. | $O(1)$ |
Index left_bound() |
Returns the domain’s left endpoint. | $O(1)$ |
Index right_bound() |
Returns the domain’s right endpoint. | $O(1)$ |
const T& initial_value() |
Returns the uniform initial leaf value. | $O(1)$ |
void reserve(size_t n) |
Reserves space for n allocated nodes. |
$O(K)$ |
size_t node_count() |
Returns the number of allocated nodes. | $O(1)$ |
void clear() |
Restores the uniform initial state while retaining capacity. | $O(K)$ |
void set(Index p, T x) |
Assigns x to coordinate p. |
$O(\log U)$ |
T get(Index p) |
Returns the value at p. |
$O(\log U)$ |
T operator[](Index p) |
Equivalent to get(p). |
$O(\log U)$ |
T prod(Index l, Index r) |
Returns the value-monoid product over [l, r). |
$O(\log U)$ |
T all_prod() |
Returns the product over the entire domain. | $O(1)$ |
void apply(Index p, F f) |
Applies f at one coordinate. |
$O(\log U)$ |
void apply(Index l, Index r, F f) |
Applies f over [l, r). |
$O(\log U)$ |
Index max_right(Index l, G g) |
Finds the largest r for which g(prod(l, r)) is true. |
$O(\log U)$ |
Index min_left(Index r, G g) |
Finds the smallest l for which g(prod(l, r)) is true. |
$O(\log U)$ |
Here $K$ is the current number of allocated nodes. After $Q$ updates, memory
usage is $O(Q \log U)$ in the worst case. max_right and min_left require the
same predicate conditions as LazySegtree: the identity must satisfy the
predicate, and the predicate must be monotone along searched products.
Example
#include "acted_monoid/range_add_range_sum.hpp"
#include "ds/segtree/dynamic_lazy_segtree.hpp"
#include <iostream>
int main() {
using AM = m1une::acted_monoid::RangeAddRangeSum<long long>;
using Seg = m1une::ds::DynamicLazySegtree<AM>;
Seg seg(-1'000'000'000LL, 1'000'000'001LL, AM::make(0));
seg.reserve(512);
seg.apply(-20, 30, 5);
seg.set(0, AM::make(100));
std::cout << seg.prod(-10, 10).sum << "\n"; // 195
}
Depends on
Verified with
Code
#ifndef M1UNE_DYNAMIC_LAZY_SEGTREE_HPP
#define M1UNE_DYNAMIC_LAZY_SEGTREE_HPP 1
#include <cassert>
#include <concepts>
#include <cstddef>
#include <limits>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#include "../../acted_monoid/concept.hpp"
#include "dynamic_segtree_common.hpp"
namespace m1une {
namespace ds {
// A sparse lazy segment tree over an integral half-open interval.
template <m1une::acted_monoid::IsActedMonoid ActedMonoid, std::integral Index = long long>
requires(!std::same_as<std::remove_cv_t<Index>, bool>)
struct DynamicLazySegtree {
using T = typename ActedMonoid::value_type;
using F = typename ActedMonoid::operator_type;
using index_type = Index;
using size_type = detail::dynamic_size_type<Index>;
private:
struct Node {
T val;
F lazy;
int left;
int right;
bool has_lazy;
explicit Node(T value)
: val(std::move(value)),
lazy(ActedMonoid::op_id()),
left(0),
right(0),
has_lazy(false) {}
};
detail::UniformMonoidDomain<ActedMonoid, Index> _domain;
int _root;
std::vector<Node> _nodes;
int new_node(Index left, Index right, int depth) {
assert(_nodes.size() < std::size_t(std::numeric_limits<int>::max()));
_nodes.emplace_back(_domain.default_product(depth, left, right));
return int(_nodes.size()) - 1;
}
const T& value(int t, Index left, Index right, int depth) const {
if (t) return _nodes[t].val;
return _domain.default_product(depth, left, right);
}
void all_apply(int& t, Index left, Index right, int depth, const F& f) {
if (!t) t = new_node(left, right, depth);
Node& node = _nodes[t];
node.val = detail::dynamic_mapping<ActedMonoid>(f, node.val);
if (std::midpoint(left, right) != left) {
node.lazy = ActedMonoid::op_comp(f, node.lazy);
node.has_lazy = true;
}
}
void push(int t, Index left, Index right, int depth) {
if (!_nodes[t].has_lazy) return;
Index middle = std::midpoint(left, right);
if (middle == left) return;
F lazy = _nodes[t].lazy;
int left_child = _nodes[t].left;
int right_child = _nodes[t].right;
all_apply(left_child, left, middle, depth + 1, lazy);
all_apply(
right_child,
middle,
right,
depth + 1,
detail::dynamic_shift<ActedMonoid>(lazy, detail::dynamic_distance(left, middle))
);
Node& node = _nodes[t];
node.left = left_child;
node.right = right_child;
node.lazy = ActedMonoid::op_id();
node.has_lazy = false;
}
void update(int t, Index left, Index right, int depth) {
Index middle = std::midpoint(left, right);
_nodes[t].val = ActedMonoid::op(
value(_nodes[t].left, left, middle, depth + 1),
value(_nodes[t].right, middle, right, depth + 1)
);
}
int set_node(int t, Index left, Index right, int depth, Index p, T x) {
if (!t) t = new_node(left, right, depth);
Index middle = std::midpoint(left, right);
if (middle == left) {
Node& node = _nodes[t];
node.val = std::move(x);
node.lazy = ActedMonoid::op_id();
node.has_lazy = false;
return t;
}
push(t, left, right, depth);
if (p < middle) {
int child = set_node(_nodes[t].left, left, middle, depth + 1, p, std::move(x));
_nodes[t].left = child;
} else {
int child = set_node(_nodes[t].right, middle, right, depth + 1, p, std::move(x));
_nodes[t].right = child;
}
update(t, left, right, depth);
return t;
}
int apply_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
Index query_right,
const F& f
) {
if (query_right <= left || right <= query_left) return t;
if (query_left <= left && right <= query_right) {
all_apply(
t,
left,
right,
depth,
detail::dynamic_shift<ActedMonoid>(f, detail::dynamic_distance(query_left, left))
);
return t;
}
if (!t) t = new_node(left, right, depth);
push(t, left, right, depth);
Index middle = std::midpoint(left, right);
int left_child = apply_node(_nodes[t].left, left, middle, depth + 1, query_left, query_right, f);
int right_child = apply_node(_nodes[t].right, middle, right, depth + 1, query_left, query_right, f);
_nodes[t].left = left_child;
_nodes[t].right = right_child;
update(t, left, right, depth);
return t;
}
F compose_for_child(const F& inherited, int t, size_type offset) const {
F shifted = detail::dynamic_shift<ActedMonoid>(inherited, offset);
if (!t || !_nodes[t].has_lazy) return shifted;
return ActedMonoid::op_comp(
shifted,
detail::dynamic_shift<ActedMonoid>(_nodes[t].lazy, offset)
);
}
T prod_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
Index query_right,
const F& inherited
) const {
if (query_right <= left || right <= query_left) return ActedMonoid::id();
if (query_left <= left && right <= query_right) {
return detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
);
}
Index middle = std::midpoint(left, right);
return ActedMonoid::op(
prod_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_left,
query_right,
compose_for_child(inherited, t, 0)
),
prod_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_left,
query_right,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle))
)
);
}
template <class G>
Index max_right_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
T& product,
const F& inherited,
G& predicate
) const {
if (right <= query_left) return right;
if (query_left <= left) {
T next = ActedMonoid::op(
product,
detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
)
);
if (predicate(next)) {
product = std::move(next);
return right;
}
Index middle = std::midpoint(left, right);
if (middle == left) return left;
}
Index middle = std::midpoint(left, right);
Index result = max_right_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_left,
product,
compose_for_child(inherited, t, 0),
predicate
);
if (result < middle) return result;
return max_right_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_left,
product,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle)),
predicate
);
}
template <class G>
Index min_left_node(
int t,
Index left,
Index right,
int depth,
Index query_right,
T& product,
const F& inherited,
G& predicate
) const {
if (query_right <= left) return left;
if (right <= query_right) {
T next = ActedMonoid::op(
detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
),
product
);
if (predicate(next)) {
product = std::move(next);
return left;
}
Index middle = std::midpoint(left, right);
if (middle == left) return right;
}
Index middle = std::midpoint(left, right);
Index result = min_left_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_right,
product,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle)),
predicate
);
if (middle < result) return result;
return min_left_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_right,
product,
compose_for_child(inherited, t, 0),
predicate
);
}
public:
DynamicLazySegtree()
: DynamicLazySegtree(Index(0), Index(0), ActedMonoid::id()) {}
explicit DynamicLazySegtree(Index n)
: DynamicLazySegtree(Index(0), n, ActedMonoid::id()) {
if constexpr (std::signed_integral<Index>) assert(Index(0) <= n);
}
DynamicLazySegtree(Index left, Index right)
: DynamicLazySegtree(left, right, ActedMonoid::id()) {}
DynamicLazySegtree(Index left, Index right, T initial_value)
: _domain(left, right, std::move(initial_value)), _root(0) {
_nodes.emplace_back(ActedMonoid::id());
}
size_type size() const {
return _domain.size();
}
bool empty() const {
return _domain.empty();
}
Index left_bound() const {
return _domain.left_bound();
}
Index right_bound() const {
return _domain.right_bound();
}
const T& initial_value() const {
return _domain.initial_value();
}
void reserve(std::size_t node_capacity) {
assert(node_capacity < std::numeric_limits<std::size_t>::max());
_nodes.reserve(node_capacity + 1);
}
std::size_t node_count() const {
return _nodes.size() - 1;
}
void clear() {
_root = 0;
_nodes.erase(_nodes.begin() + 1, _nodes.end());
}
void set(Index p, T x) {
assert(left_bound() <= p && p < right_bound());
_root = set_node(_root, left_bound(), right_bound(), 0, p, std::move(x));
}
T get(Index p) const {
assert(left_bound() <= p && p < right_bound());
return prod(p, p + 1);
}
T operator[](Index p) const {
return get(p);
}
T prod(Index left, Index right) const {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return ActedMonoid::id();
return prod_node(
_root,
left_bound(),
right_bound(),
0,
left,
right,
ActedMonoid::op_id()
);
}
T all_prod() const {
return value(_root, left_bound(), right_bound(), 0);
}
void apply(Index p, const F& f) {
assert(left_bound() <= p && p < right_bound());
apply(p, p + 1, f);
}
void apply(Index left, Index right, const F& f) {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return;
_root = apply_node(
_root,
left_bound(),
right_bound(),
0,
left,
right,
f
);
}
template <class G>
Index max_right(Index left, G predicate) const {
assert(left_bound() <= left && left <= right_bound());
assert(predicate(ActedMonoid::id()));
if (left == right_bound()) return right_bound();
T product = ActedMonoid::id();
return max_right_node(
_root,
left_bound(),
right_bound(),
0,
left,
product,
ActedMonoid::op_id(),
predicate
);
}
template <class G>
Index min_left(Index right, G predicate) const {
assert(left_bound() <= right && right <= right_bound());
assert(predicate(ActedMonoid::id()));
if (right == left_bound()) return left_bound();
T product = ActedMonoid::id();
return min_left_node(
_root,
left_bound(),
right_bound(),
0,
right,
product,
ActedMonoid::op_id(),
predicate
);
}
};
} // namespace ds
} // namespace m1une
#endif // M1UNE_DYNAMIC_LAZY_SEGTREE_HPP#line 1 "ds/segtree/dynamic_lazy_segtree.hpp"
#include <cassert>
#include <concepts>
#include <cstddef>
#include <limits>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#line 1 "acted_monoid/concept.hpp"
#line 5 "acted_monoid/concept.hpp"
namespace m1une {
namespace acted_monoid {
// Concept defining the requirements for an Acted Monoid.
template <typename AM>
concept IsActedMonoid = requires(typename AM::value_type a, typename AM::value_type b, typename AM::operator_type f,
typename AM::operator_type g) {
// 1. Value Monoid
typename AM::value_type;
{ AM::id() } -> std::same_as<typename AM::value_type>;
{ AM::op(a, b) } -> std::same_as<typename AM::value_type>;
// 2. Operator Monoid
typename AM::operator_type;
{ AM::op_id() } -> std::same_as<typename AM::operator_type>;
{ AM::op_comp(f, g) } -> std::same_as<typename AM::operator_type>; // Composition order: f(g(x))
// 3. Mapping: Operator x Value -> Value
{ AM::mapping(f, a) } -> std::same_as<typename AM::value_type>;
};
// Concept for acted monoids whose value monoid is a commutative group.
// The value operation must obey commutativity and inverse laws.
template <typename AM>
concept IsCommutativeActedGroup = IsActedMonoid<AM> && requires(typename AM::value_type a) {
{ AM::inv(a) } -> std::same_as<typename AM::value_type>;
};
} // namespace acted_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 15 "ds/segtree/dynamic_lazy_segtree.hpp"
namespace m1une {
namespace ds {
// A sparse lazy segment tree over an integral half-open interval.
template <m1une::acted_monoid::IsActedMonoid ActedMonoid, std::integral Index = long long>
requires(!std::same_as<std::remove_cv_t<Index>, bool>)
struct DynamicLazySegtree {
using T = typename ActedMonoid::value_type;
using F = typename ActedMonoid::operator_type;
using index_type = Index;
using size_type = detail::dynamic_size_type<Index>;
private:
struct Node {
T val;
F lazy;
int left;
int right;
bool has_lazy;
explicit Node(T value)
: val(std::move(value)),
lazy(ActedMonoid::op_id()),
left(0),
right(0),
has_lazy(false) {}
};
detail::UniformMonoidDomain<ActedMonoid, Index> _domain;
int _root;
std::vector<Node> _nodes;
int new_node(Index left, Index right, int depth) {
assert(_nodes.size() < std::size_t(std::numeric_limits<int>::max()));
_nodes.emplace_back(_domain.default_product(depth, left, right));
return int(_nodes.size()) - 1;
}
const T& value(int t, Index left, Index right, int depth) const {
if (t) return _nodes[t].val;
return _domain.default_product(depth, left, right);
}
void all_apply(int& t, Index left, Index right, int depth, const F& f) {
if (!t) t = new_node(left, right, depth);
Node& node = _nodes[t];
node.val = detail::dynamic_mapping<ActedMonoid>(f, node.val);
if (std::midpoint(left, right) != left) {
node.lazy = ActedMonoid::op_comp(f, node.lazy);
node.has_lazy = true;
}
}
void push(int t, Index left, Index right, int depth) {
if (!_nodes[t].has_lazy) return;
Index middle = std::midpoint(left, right);
if (middle == left) return;
F lazy = _nodes[t].lazy;
int left_child = _nodes[t].left;
int right_child = _nodes[t].right;
all_apply(left_child, left, middle, depth + 1, lazy);
all_apply(
right_child,
middle,
right,
depth + 1,
detail::dynamic_shift<ActedMonoid>(lazy, detail::dynamic_distance(left, middle))
);
Node& node = _nodes[t];
node.left = left_child;
node.right = right_child;
node.lazy = ActedMonoid::op_id();
node.has_lazy = false;
}
void update(int t, Index left, Index right, int depth) {
Index middle = std::midpoint(left, right);
_nodes[t].val = ActedMonoid::op(
value(_nodes[t].left, left, middle, depth + 1),
value(_nodes[t].right, middle, right, depth + 1)
);
}
int set_node(int t, Index left, Index right, int depth, Index p, T x) {
if (!t) t = new_node(left, right, depth);
Index middle = std::midpoint(left, right);
if (middle == left) {
Node& node = _nodes[t];
node.val = std::move(x);
node.lazy = ActedMonoid::op_id();
node.has_lazy = false;
return t;
}
push(t, left, right, depth);
if (p < middle) {
int child = set_node(_nodes[t].left, left, middle, depth + 1, p, std::move(x));
_nodes[t].left = child;
} else {
int child = set_node(_nodes[t].right, middle, right, depth + 1, p, std::move(x));
_nodes[t].right = child;
}
update(t, left, right, depth);
return t;
}
int apply_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
Index query_right,
const F& f
) {
if (query_right <= left || right <= query_left) return t;
if (query_left <= left && right <= query_right) {
all_apply(
t,
left,
right,
depth,
detail::dynamic_shift<ActedMonoid>(f, detail::dynamic_distance(query_left, left))
);
return t;
}
if (!t) t = new_node(left, right, depth);
push(t, left, right, depth);
Index middle = std::midpoint(left, right);
int left_child = apply_node(_nodes[t].left, left, middle, depth + 1, query_left, query_right, f);
int right_child = apply_node(_nodes[t].right, middle, right, depth + 1, query_left, query_right, f);
_nodes[t].left = left_child;
_nodes[t].right = right_child;
update(t, left, right, depth);
return t;
}
F compose_for_child(const F& inherited, int t, size_type offset) const {
F shifted = detail::dynamic_shift<ActedMonoid>(inherited, offset);
if (!t || !_nodes[t].has_lazy) return shifted;
return ActedMonoid::op_comp(
shifted,
detail::dynamic_shift<ActedMonoid>(_nodes[t].lazy, offset)
);
}
T prod_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
Index query_right,
const F& inherited
) const {
if (query_right <= left || right <= query_left) return ActedMonoid::id();
if (query_left <= left && right <= query_right) {
return detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
);
}
Index middle = std::midpoint(left, right);
return ActedMonoid::op(
prod_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_left,
query_right,
compose_for_child(inherited, t, 0)
),
prod_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_left,
query_right,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle))
)
);
}
template <class G>
Index max_right_node(
int t,
Index left,
Index right,
int depth,
Index query_left,
T& product,
const F& inherited,
G& predicate
) const {
if (right <= query_left) return right;
if (query_left <= left) {
T next = ActedMonoid::op(
product,
detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
)
);
if (predicate(next)) {
product = std::move(next);
return right;
}
Index middle = std::midpoint(left, right);
if (middle == left) return left;
}
Index middle = std::midpoint(left, right);
Index result = max_right_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_left,
product,
compose_for_child(inherited, t, 0),
predicate
);
if (result < middle) return result;
return max_right_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_left,
product,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle)),
predicate
);
}
template <class G>
Index min_left_node(
int t,
Index left,
Index right,
int depth,
Index query_right,
T& product,
const F& inherited,
G& predicate
) const {
if (query_right <= left) return left;
if (right <= query_right) {
T next = ActedMonoid::op(
detail::dynamic_mapping<ActedMonoid>(
inherited,
value(t, left, right, depth)
),
product
);
if (predicate(next)) {
product = std::move(next);
return left;
}
Index middle = std::midpoint(left, right);
if (middle == left) return right;
}
Index middle = std::midpoint(left, right);
Index result = min_left_node(
t ? _nodes[t].right : 0,
middle,
right,
depth + 1,
query_right,
product,
compose_for_child(inherited, t, detail::dynamic_distance(left, middle)),
predicate
);
if (middle < result) return result;
return min_left_node(
t ? _nodes[t].left : 0,
left,
middle,
depth + 1,
query_right,
product,
compose_for_child(inherited, t, 0),
predicate
);
}
public:
DynamicLazySegtree()
: DynamicLazySegtree(Index(0), Index(0), ActedMonoid::id()) {}
explicit DynamicLazySegtree(Index n)
: DynamicLazySegtree(Index(0), n, ActedMonoid::id()) {
if constexpr (std::signed_integral<Index>) assert(Index(0) <= n);
}
DynamicLazySegtree(Index left, Index right)
: DynamicLazySegtree(left, right, ActedMonoid::id()) {}
DynamicLazySegtree(Index left, Index right, T initial_value)
: _domain(left, right, std::move(initial_value)), _root(0) {
_nodes.emplace_back(ActedMonoid::id());
}
size_type size() const {
return _domain.size();
}
bool empty() const {
return _domain.empty();
}
Index left_bound() const {
return _domain.left_bound();
}
Index right_bound() const {
return _domain.right_bound();
}
const T& initial_value() const {
return _domain.initial_value();
}
void reserve(std::size_t node_capacity) {
assert(node_capacity < std::numeric_limits<std::size_t>::max());
_nodes.reserve(node_capacity + 1);
}
std::size_t node_count() const {
return _nodes.size() - 1;
}
void clear() {
_root = 0;
_nodes.erase(_nodes.begin() + 1, _nodes.end());
}
void set(Index p, T x) {
assert(left_bound() <= p && p < right_bound());
_root = set_node(_root, left_bound(), right_bound(), 0, p, std::move(x));
}
T get(Index p) const {
assert(left_bound() <= p && p < right_bound());
return prod(p, p + 1);
}
T operator[](Index p) const {
return get(p);
}
T prod(Index left, Index right) const {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return ActedMonoid::id();
return prod_node(
_root,
left_bound(),
right_bound(),
0,
left,
right,
ActedMonoid::op_id()
);
}
T all_prod() const {
return value(_root, left_bound(), right_bound(), 0);
}
void apply(Index p, const F& f) {
assert(left_bound() <= p && p < right_bound());
apply(p, p + 1, f);
}
void apply(Index left, Index right, const F& f) {
assert(left_bound() <= left && left <= right && right <= right_bound());
if (left == right) return;
_root = apply_node(
_root,
left_bound(),
right_bound(),
0,
left,
right,
f
);
}
template <class G>
Index max_right(Index left, G predicate) const {
assert(left_bound() <= left && left <= right_bound());
assert(predicate(ActedMonoid::id()));
if (left == right_bound()) return right_bound();
T product = ActedMonoid::id();
return max_right_node(
_root,
left_bound(),
right_bound(),
0,
left,
product,
ActedMonoid::op_id(),
predicate
);
}
template <class G>
Index min_left(Index right, G predicate) const {
assert(left_bound() <= right && right <= right_bound());
assert(predicate(ActedMonoid::id()));
if (right == left_bound()) return left_bound();
T product = ActedMonoid::id();
return min_left_node(
_root,
left_bound(),
right_bound(),
0,
right,
product,
ActedMonoid::op_id(),
predicate
);
}
};
} // namespace ds
} // namespace m1une