m1une's library

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

View on GitHub

:heavy_check_mark: verify/beats_acted_monoid/range_bitwise_and_or_range_sum.test.cpp

Depends on

Code

#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <limits>
#include <vector>

#include "../../beats_acted_monoid/concept.hpp"
#include "../../beats_acted_monoid/range_bitwise_and_or_range_sum.hpp"
#include "../../ds/segtree/segtree_beats.hpp"

namespace {

using AM =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<long long, 10>;
using SignedFullWidth =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<long long, 63>;
using UnsignedFullWidth =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<
        unsigned long long,
        64
    >;

long long apply_scalar(const AM::operator_type& f, long long value) {
    return (value & f.and_mask) | f.or_mask;
}

void test_composition() {
    std::vector<AM::operator_type> operators;
    for (long long mask = 0; mask < 32; ++mask) {
        operators.emplace_back(AM::make_and(mask));
        operators.emplace_back(AM::make_or(mask));
    }

    for (const auto& f : operators) {
        for (const auto& g : operators) {
            auto composition = AM::op_comp(f, g);
            for (long long value = 0; value < 32; ++value) {
                assert(
                    apply_scalar(composition, value) ==
                    apply_scalar(f, apply_scalar(g, value))
                );
            }
        }
    }
}

void test_randomized() {
    constexpr int size = 73;
    constexpr long long mask = (1LL << 10) - 1;
    std::uint64_t state = 0x3141592653589793ULL;
    auto random = [&state]() {
        state ^= state << 7;
        state ^= state >> 9;
        return state;
    };

    std::vector<long long> values(size);
    for (long long& value : values) {
        value = static_cast<long long>(random() & mask);
    }
    m1une::ds::SegtreeBeats<AM> seg(values);

    for (int step = 0; step < 10000; ++step) {
        int left = int(random() % (size + 1));
        int right = int(random() % (size + 1));
        if (right < left) std::swap(left, right);

        if (random() % 3 != 0) {
            long long operand = static_cast<long long>(random() & mask);
            if (random() & 1) {
                seg.apply(left, right, AM::make_and(operand));
                for (int index = left; index < right; ++index) {
                    values[index] &= operand;
                }
            } else {
                seg.apply(left, right, AM::make_or(operand));
                for (int index = left; index < right; ++index) {
                    values[index] |= operand;
                }
            }
        } else {
            long long expected = 0;
            for (int index = left; index < right; ++index) {
                expected += values[index];
            }
            assert(seg.prod(left, right).sum == expected);
        }

        int index = int(random() % size);
        assert(seg.get(index).sum == values[index]);
    }
}

static_assert(m1une::beats_acted_monoid::IsBeatsActedMonoid<AM>);
static_assert(AM::commutative);
static_assert(!AM::operator_commutative);
static_assert(
    SignedFullWidth::bit_mask() ==
    std::numeric_limits<long long>::max()
);
static_assert(
    UnsignedFullWidth::bit_mask() ==
    std::numeric_limits<unsigned long long>::max()
);

}  // namespace

int main() {
    test_composition();
    test_randomized();

    long long a, b;
    std::cin >> a >> b;
    std::cout << a + b << '\n';
}
#line 1 "verify/beats_acted_monoid/range_bitwise_and_or_range_sum.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/aplusb"

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <limits>
#include <vector>

#line 1 "beats_acted_monoid/concept.hpp"



#include <concepts>

#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 7 "beats_acted_monoid/concept.hpp"

namespace m1une {
namespace beats_acted_monoid {

// An acted monoid whose action may require descent before it can be applied.
template <typename AM>
concept IsBeatsActedMonoid = m1une::acted_monoid::IsActedMonoid<AM> &&
    requires(typename AM::value_type x, typename AM::operator_type f) {
        { AM::can_apply(f, x) } -> std::same_as<bool>;
    };

}  // namespace beats_acted_monoid
}  // namespace m1une


#line 1 "beats_acted_monoid/range_bitwise_and_or_range_sum.hpp"



#line 6 "beats_acted_monoid/range_bitwise_and_or_range_sum.hpp"
#include <type_traits>

namespace m1une {
namespace beats_acted_monoid {

template <typename T>
struct RangeBitwiseAndOrRangeSumNode {
    T sum;
    T bitwise_and;
    T bitwise_or;
    long long length;
};

// Beats acted monoid for range bitwise AND/OR updates and range sum queries.
template <typename T, int BITS = 30>
struct RangeBitwiseAndOrRangeSum {
    static_assert(
        std::is_integral_v<T> &&
        !std::is_same_v<std::remove_cv_t<T>, bool>
    );
    static_assert(0 < BITS && BITS <= std::numeric_limits<T>::digits);

    using value_type = RangeBitwiseAndOrRangeSumNode<T>;

    // Represents f(x) = (x & and_mask) | or_mask.
    struct operator_type {
        T and_mask;
        T or_mask;
    };

    static constexpr bool commutative = true;
    static constexpr bool operator_commutative = false;

    static constexpr T bit_mask() {
        if constexpr (
            std::is_unsigned_v<T> &&
            BITS == std::numeric_limits<T>::digits
        ) {
            return ~T(0);
        } else {
            return
                (T(1) << (BITS - 1)) |
                ((T(1) << (BITS - 1)) - 1);
        }
    }

    static constexpr value_type id() {
        return {T(0), bit_mask(), T(0), 0};
    }

    static constexpr value_type op(
        const value_type& left,
        const value_type& right
    ) {
        return {
            left.sum + right.sum,
            left.bitwise_and & right.bitwise_and,
            left.bitwise_or | right.bitwise_or,
            left.length + right.length
        };
    }

    static constexpr operator_type op_id() {
        return {bit_mask(), T(0)};
    }

    // Returns f(g(x)).
    static constexpr operator_type op_comp(
        const operator_type& f,
        const operator_type& g
    ) {
        return {
            (f.and_mask & g.and_mask) & bit_mask(),
            ((g.or_mask & f.and_mask) | f.or_mask) & bit_mask()
        };
    }

    static constexpr bool can_apply(
        const operator_type& f,
        const value_type& value
    ) {
        if (value.length == 0) return true;
        T changed = ((~f.and_mask) | f.or_mask) & bit_mask();
        T mixed = value.bitwise_and ^ value.bitwise_or;
        return (changed & mixed) == T(0);
    }

    static constexpr value_type mapping(
        const operator_type& f,
        const value_type& value
    ) {
        assert(can_apply(f, value));
        if (value.length == 0) return value;
        T changed = ((~f.and_mask) | f.or_mask) & bit_mask();
        T old_uniform = value.bitwise_and & changed;
        T new_uniform =
            ((old_uniform & f.and_mask) | f.or_mask) & changed;

        value_type result = value;
        result.sum +=
            (new_uniform - old_uniform) * T(value.length);
        result.bitwise_and =
            ((value.bitwise_and & f.and_mask) | f.or_mask) & bit_mask();
        result.bitwise_or =
            ((value.bitwise_or & f.and_mask) | f.or_mask) & bit_mask();
        return result;
    }

    static constexpr value_type make(const T& value) {
        assert((value & ~bit_mask()) == T(0));
        return {value, value, value, 1};
    }

    static constexpr operator_type make_and(const T& mask) {
        return {mask & bit_mask(), T(0)};
    }

    static constexpr operator_type make_or(const T& mask) {
        return {bit_mask(), mask & bit_mask()};
    }
};

}  // namespace beats_acted_monoid
}  // namespace m1une


#line 1 "ds/segtree/segtree_beats.hpp"



#line 6 "ds/segtree/segtree_beats.hpp"
#include <utility>
#line 8 "ds/segtree/segtree_beats.hpp"

#line 1 "math/bit_ceil.hpp"



namespace m1une {
namespace math {

template <typename T>
constexpr T bit_ceil(T n) {
    if (n <= 1) return 1;
    T x = 1;
    while (x < n) x <<= 1;
    return x;
}

}  // namespace math
}  // namespace m1une


#line 11 "ds/segtree/segtree_beats.hpp"

namespace m1une {
namespace ds {

// Generic Segment Tree Beats for actions that may require recursive descent.
template <m1une::beats_acted_monoid::IsBeatsActedMonoid ActedMonoid>
struct SegtreeBeats {
    using value_type = typename ActedMonoid::value_type;
    using operator_type = typename ActedMonoid::operator_type;
    using T = value_type;
    using F = operator_type;

   private:
    int _n = 0;
    int _size = 1;
    std::vector<T> _data;
    std::vector<F> _lazy;

    static T mapping_at(const F& f, const T& value, long long ordinal) {
        if constexpr (requires(F g, T x, long long i) {
            ActedMonoid::mapping(g, x, i);
        }) {
            return ActedMonoid::mapping(f, value, ordinal);
        } else {
            return ActedMonoid::mapping(f, value);
        }
    }

    static bool can_apply_at(const F& f, const T& value, long long ordinal) {
        if constexpr (requires(F g, T x, long long i) {
            ActedMonoid::can_apply(g, x, i);
        }) {
            return ActedMonoid::can_apply(f, value, ordinal);
        } else {
            return ActedMonoid::can_apply(f, value);
        }
    }

    static F shift_operator(const F& f, long long ordinal) {
        if constexpr (requires(F g, long long i) {
            ActedMonoid::op_shift(g, i);
        }) {
            return ActedMonoid::op_shift(f, ordinal);
        } else {
            return f;
        }
    }

    void initialize(std::vector<T>&& values) {
        _n = int(values.size());
        _size = int(m1une::math::bit_ceil((unsigned int)_n));
        _data.assign(2 * _size, ActedMonoid::id());
        _lazy.assign(_size, ActedMonoid::op_id());
        for (int i = 0; i < _n; ++i) {
            _data[_size + i] = std::move(values[i]);
        }
        for (int k = _size - 1; k >= 1; --k) update(k);
    }

    void update(int node) {
        _data[node] = ActedMonoid::op(
            _data[node * 2],
            _data[node * 2 + 1]
        );
    }

    void all_apply(int node, int left, int right, const F& f) {
        if (_n <= left) return;
        if (can_apply_at(f, _data[node], 0)) {
            _data[node] = mapping_at(f, _data[node], 0);
            if (node < _size) {
                _lazy[node] = ActedMonoid::op_comp(f, _lazy[node]);
            }
            return;
        }

        assert(right - left > 1);
        push(node, left, right);
        int middle = left + (right - left) / 2;
        all_apply(node * 2, left, middle, f);
        all_apply(
            node * 2 + 1,
            middle,
            right,
            shift_operator(f, middle - left)
        );
        update(node);
    }

    void push(int node, int left, int right) {
        assert(right - left > 1);
        int middle = left + (right - left) / 2;
        F f = _lazy[node];
        _lazy[node] = ActedMonoid::op_id();
        all_apply(node * 2, left, middle, f);
        all_apply(
            node * 2 + 1,
            middle,
            right,
            shift_operator(f, middle - left)
        );
    }

    void set_impl(
        int node,
        int left,
        int right,
        int index,
        T value
    ) {
        if (right - left == 1) {
            _data[node] = std::move(value);
            return;
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        if (index < middle) {
            set_impl(node * 2, left, middle, index, std::move(value));
        } else {
            set_impl(
                node * 2 + 1,
                middle,
                right,
                index,
                std::move(value)
            );
        }
        update(node);
    }

    T get_impl(int node, int left, int right, int index) {
        if (right - left == 1) return _data[node];
        push(node, left, right);
        int middle = left + (right - left) / 2;
        if (index < middle) {
            return get_impl(node * 2, left, middle, index);
        }
        return get_impl(node * 2 + 1, middle, right, index);
    }

    T prod_impl(
        int node,
        int left,
        int right,
        int query_left,
        int query_right
    ) {
        if (
            query_right <= left || right <= query_left || _n <= left
        ) {
            return ActedMonoid::id();
        }
        if (query_left <= left && right <= query_right) {
            return _data[node];
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        return ActedMonoid::op(
            prod_impl(
                node * 2,
                left,
                middle,
                query_left,
                query_right
            ),
            prod_impl(
                node * 2 + 1,
                middle,
                right,
                query_left,
                query_right
            )
        );
    }

    void apply_impl(
        int node,
        int left,
        int right,
        int query_left,
        int query_right,
        int base_left,
        const F& f
    ) {
        if (
            query_right <= left || right <= query_left || _n <= left
        ) {
            return;
        }
        if (query_left <= left && right <= query_right) {
            all_apply(
                node,
                left,
                right,
                shift_operator(f, left - base_left)
            );
            return;
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        apply_impl(
            node * 2,
            left,
            middle,
            query_left,
            query_right,
            base_left,
            f
        );
        apply_impl(
            node * 2 + 1,
            middle,
            right,
            query_left,
            query_right,
            base_left,
            f
        );
        update(node);
    }

    void collect_impl(
        int node,
        int left,
        int right,
        int query_left,
        int query_right,
        std::vector<T>& result
    ) {
        if (
            query_right <= left || right <= query_left || _n <= left
        ) {
            return;
        }
        if (right - left == 1) {
            result.push_back(_data[node]);
            return;
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        collect_impl(
            node * 2,
            left,
            middle,
            query_left,
            query_right,
            result
        );
        collect_impl(
            node * 2 + 1,
            middle,
            right,
            query_left,
            query_right,
            result
        );
    }

    template <class Predicate>
    bool max_right_impl(
        int node,
        int left,
        int right,
        int query_left,
        Predicate& predicate,
        T& product,
        int& answer
    ) {
        if (right <= query_left || _n <= left) return true;
        if (query_left <= left) {
            T next = ActedMonoid::op(product, _data[node]);
            if (predicate(next)) {
                product = std::move(next);
                return true;
            }
            if (right - left == 1) {
                answer = left;
                return false;
            }
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        if (!max_right_impl(
                node * 2,
                left,
                middle,
                query_left,
                predicate,
                product,
                answer
            )) {
            return false;
        }
        return max_right_impl(
            node * 2 + 1,
            middle,
            right,
            query_left,
            predicate,
            product,
            answer
        );
    }

    template <class Predicate>
    bool min_left_impl(
        int node,
        int left,
        int right,
        int query_right,
        Predicate& predicate,
        T& product,
        int& answer
    ) {
        if (query_right <= left || _n <= left) return true;
        if (right <= query_right) {
            T next = ActedMonoid::op(_data[node], product);
            if (predicate(next)) {
                product = std::move(next);
                return true;
            }
            if (right - left == 1) {
                answer = right;
                return false;
            }
        }
        push(node, left, right);
        int middle = left + (right - left) / 2;
        if (!min_left_impl(
                node * 2 + 1,
                middle,
                right,
                query_right,
                predicate,
                product,
                answer
            )) {
            return false;
        }
        return min_left_impl(
            node * 2,
            left,
            middle,
            query_right,
            predicate,
            product,
            answer
        );
    }

   public:
    SegtreeBeats() {
        initialize({});
    }

    explicit SegtreeBeats(int n) {
        assert(0 <= n);
        initialize(std::vector<T>(n, ActedMonoid::id()));
    }

    explicit SegtreeBeats(const std::vector<T>& values) {
        initialize(std::vector<T>(values));
    }

    explicit SegtreeBeats(std::vector<T>&& values) {
        initialize(std::move(values));
    }

    template <typename U>
    requires (!std::same_as<U, T>) && (
        requires(U x) { ActedMonoid::make(x); } ||
        requires(U x, int i) { ActedMonoid::make(x, i); } ||
        std::convertible_to<U, T>
    )
    explicit SegtreeBeats(const std::vector<U>& values) {
        std::vector<T> converted;
        converted.reserve(values.size());
        for (int i = 0; i < int(values.size()); ++i) {
            if constexpr (requires(U x) { ActedMonoid::make(x); }) {
                converted.push_back(ActedMonoid::make(values[i]));
            } else if constexpr (requires(U x, int index) {
                ActedMonoid::make(x, index);
            }) {
                converted.push_back(ActedMonoid::make(values[i], i));
            } else {
                converted.push_back(static_cast<T>(values[i]));
            }
        }
        initialize(std::move(converted));
    }

    int size() const {
        return _n;
    }

    bool empty() const {
        return _n == 0;
    }

    void set(int index, T value) {
        assert(0 <= index && index < _n);
        set_impl(1, 0, _size, index, std::move(value));
    }

    T get(int index) {
        assert(0 <= index && index < _n);
        return get_impl(1, 0, _size, index);
    }

    T operator[](int index) {
        return get(index);
    }

    T prod(int left, int right) {
        assert(0 <= left && left <= right && right <= _n);
        if (left == right) return ActedMonoid::id();
        return prod_impl(1, 0, _size, left, right);
    }

    T all_prod() const {
        return _data[1];
    }

    void apply(int index, F f) {
        assert(0 <= index && index < _n);
        apply_impl(1, 0, _size, index, index + 1, index, f);
    }

    void apply(int left, int right, F f) {
        assert(0 <= left && left <= right && right <= _n);
        if (left == right) return;
        apply_impl(1, 0, _size, left, right, left, f);
    }

    std::vector<T> to_vector() {
        return to_vector(0, _n);
    }

    std::vector<T> to_vector(int left, int right) {
        assert(0 <= left && left <= right && right <= _n);
        std::vector<T> result;
        result.reserve(right - left);
        collect_impl(1, 0, _size, left, right, result);
        return result;
    }

    template <class Predicate>
    int max_right(int left, Predicate predicate) {
        assert(0 <= left && left <= _n);
        assert(predicate(ActedMonoid::id()));
        if (left == _n) return _n;
        T product = ActedMonoid::id();
        int answer = _n;
        max_right_impl(
            1,
            0,
            _size,
            left,
            predicate,
            product,
            answer
        );
        return answer;
    }

    template <class Predicate>
    int min_left(int right, Predicate predicate) {
        assert(0 <= right && right <= _n);
        assert(predicate(ActedMonoid::id()));
        if (right == 0) return 0;
        T product = ActedMonoid::id();
        int answer = 0;
        min_left_impl(
            1,
            0,
            _size,
            right,
            predicate,
            product,
            answer
        );
        return answer;
    }
};

}  // namespace ds
}  // namespace m1une


#line 13 "verify/beats_acted_monoid/range_bitwise_and_or_range_sum.test.cpp"

namespace {

using AM =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<long long, 10>;
using SignedFullWidth =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<long long, 63>;
using UnsignedFullWidth =
    m1une::beats_acted_monoid::RangeBitwiseAndOrRangeSum<
        unsigned long long,
        64
    >;

long long apply_scalar(const AM::operator_type& f, long long value) {
    return (value & f.and_mask) | f.or_mask;
}

void test_composition() {
    std::vector<AM::operator_type> operators;
    for (long long mask = 0; mask < 32; ++mask) {
        operators.emplace_back(AM::make_and(mask));
        operators.emplace_back(AM::make_or(mask));
    }

    for (const auto& f : operators) {
        for (const auto& g : operators) {
            auto composition = AM::op_comp(f, g);
            for (long long value = 0; value < 32; ++value) {
                assert(
                    apply_scalar(composition, value) ==
                    apply_scalar(f, apply_scalar(g, value))
                );
            }
        }
    }
}

void test_randomized() {
    constexpr int size = 73;
    constexpr long long mask = (1LL << 10) - 1;
    std::uint64_t state = 0x3141592653589793ULL;
    auto random = [&state]() {
        state ^= state << 7;
        state ^= state >> 9;
        return state;
    };

    std::vector<long long> values(size);
    for (long long& value : values) {
        value = static_cast<long long>(random() & mask);
    }
    m1une::ds::SegtreeBeats<AM> seg(values);

    for (int step = 0; step < 10000; ++step) {
        int left = int(random() % (size + 1));
        int right = int(random() % (size + 1));
        if (right < left) std::swap(left, right);

        if (random() % 3 != 0) {
            long long operand = static_cast<long long>(random() & mask);
            if (random() & 1) {
                seg.apply(left, right, AM::make_and(operand));
                for (int index = left; index < right; ++index) {
                    values[index] &= operand;
                }
            } else {
                seg.apply(left, right, AM::make_or(operand));
                for (int index = left; index < right; ++index) {
                    values[index] |= operand;
                }
            }
        } else {
            long long expected = 0;
            for (int index = left; index < right; ++index) {
                expected += values[index];
            }
            assert(seg.prod(left, right).sum == expected);
        }

        int index = int(random() % size);
        assert(seg.get(index).sum == values[index]);
    }
}

static_assert(m1une::beats_acted_monoid::IsBeatsActedMonoid<AM>);
static_assert(AM::commutative);
static_assert(!AM::operator_commutative);
static_assert(
    SignedFullWidth::bit_mask() ==
    std::numeric_limits<long long>::max()
);
static_assert(
    UnsignedFullWidth::bit_mask() ==
    std::numeric_limits<unsigned long long>::max()
);

}  // namespace

int main() {
    test_composition();
    test_randomized();

    long long a, b;
    std::cin >> a >> b;
    std::cout << a + b << '\n';
}
Back to top page