m1une's library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:heavy_check_mark: Dynamic Lazy Segment Tree
(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

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

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
Back to top page