モノイドの要件
lib/segtree/monoid.hpp
概要
値型 value_type、単位元 id()、二項演算 op() を持つこと。
インクルード
#include "segtree/monoid.hpp"
クラス
| 名前 | 説明 |
|---|---|
Add |
和モノイド |
Mul |
積モノイド |
And |
ビット積モノイド |
Or |
ビット和モノイド |
Xor |
ビット排他的論理和モノイド |
Min |
最小値モノイド |
Max |
最大値モノイド |
MaxZero |
最大値モノイド(単位元 0 固定) |
Gcd |
最大公約数モノイド |
Lcm |
最小公倍数モノイド |
Update |
上書きモノイド(後勝ち) |
Affine |
アフィン変換モノイド |
Rev |
モノイドの演算順序を反転するアダプタ |
コンセプト
| 名前 | 説明 |
|---|---|
monoid |
モノイドの要件 |
acts_on |
作用素モノイド M を値型 U に作用させられるか |
検証
- test/aoj/challenge/1508.test.cpp
- test/aoj/course/ALDS1_14_D.test.cpp
- test/aoj/course/DSL_2_A.test.cpp
- test/aoj/dsl/raq_rmq.test.cpp
- test/yosupo/data_structure/point_set_range_frequency.test.cpp
- test/yosupo/data_structure/range_reverse_range_sum.test.cpp
- test/yosupo/data_structure/static_rmq.test.cpp
- test/yosupo/number_theory/discrete_logarithm_mod.test.cpp
- test/yosupo/tree/vertex_set_path_composite.test.cpp
- test/yukicoder/1013.test.cpp
- test/yukicoder/1097.test.cpp
- test/yukicoder/1977.test.cpp
このヘッダを使うライブラリ(23)
-
ダブリング (doubling)
lib/algorithm/doubling.hpp -
Disjoint Sparse Table
lib/data_structure/disjoint_sparse_table.hpp -
動的配列(スプレー木、反転可能、遅延伝播対応)
lib/data_structure/dynamic_sequence.hpp -
線形 Sparse Table
lib/data_structure/linear_sparse_table.hpp -
領域木
lib/data_structure/range_tree.hpp -
スパーステーブル (Sparse Table)
lib/data_structure/sparse_table.hpp -
Sliding Window Aggregation
lib/data_structure/swag.hpp -
モノイド作用に関する離散対数問題
lib/number_theory/discrete_log.hpp -
永続双対セグメント木
lib/persistent_ds/persistent_dual_segment_tree.hpp -
永続セグメント木
lib/persistent_ds/persistent_segment_tree.hpp -
双対セグメント木
lib/segtree/dual_segment_tree.hpp -
動的遅延評価セグメント木
lib/segtree/dynamic_lazy_segment_tree.hpp -
動的セグメント木
lib/segtree/dynamic_segment_tree.hpp -
要素を特定のキー(条件)でフィルタリングして区間取得を行えるセグメント木
lib/segtree/filtered_segment_tree.hpp -
遅延評価セグメント木
lib/segtree/lazy_segment_tree.hpp -
セグメント木 (SegmentTree)
lib/segtree/segment_tree.hpp -
二次元セグメント木
lib/segtree/segment_tree_2d.hpp -
auxiliary tree(補助木 / virtual tree)
lib/tree/auxiliary_tree.hpp -
森への辺の追加・削除と連結成分のモノイド積を管理する Euler Tour Tree
lib/tree/euler_tour_tree.hpp -
線形前処理・定数時間クエリのLCA
lib/tree/linear_lca.hpp -
Link-Cut Tree
lib/tree/link_cut_tree.hpp -
全方位木dp
lib/tree/rerooting.hpp -
Segment Tree on Tree
lib/tree/segment_tree_on_tree.hpp
検証プログラム(41)
-
test/aoj/challenge/1508.test.cpp
test/aoj/challenge/1508.test.cpp -
test/aoj/challenge/2270.test.cpp
test/aoj/challenge/2270.test.cpp -
test/aoj/challenge/2426.test.cpp
test/aoj/challenge/2426.test.cpp -
test/aoj/challenge/2871.test.cpp
test/aoj/challenge/2871.test.cpp -
test/aoj/challenge/3163.test.cpp
test/aoj/challenge/3163.test.cpp -
test/aoj/course/ALDS1_14_D.test.cpp
test/aoj/course/ALDS1_14_D.test.cpp -
test/aoj/course/DSL_2_A.test.cpp
test/aoj/course/DSL_2_A.test.cpp -
test/aoj/dsl/raq_rmq.test.cpp
test/aoj/dsl/raq_rmq.test.cpp -
test/aoj/dsl/rmq_ruq.test.cpp
test/aoj/dsl/rmq_ruq.test.cpp -
test/aoj/dsl/rsq_ruq.test.cpp
test/aoj/dsl/rsq_ruq.test.cpp -
test/aoj/dsl/ruq.test.cpp
test/aoj/dsl/ruq.test.cpp -
test/aoj/grl/tree_height.test.cpp
test/aoj/grl/tree_height.test.cpp -
test/aoj/jag/doubling.test.cpp
test/aoj/jag/doubling.test.cpp -
test/yosupo/data_structure/dynamic_sequence_range_affine_range_sum.test.cpp
test/yosupo/data_structure/dynamic_sequence_range_affine_range_sum.test.cpp -
test/yosupo/data_structure/dynamic_tree_vertex_add_path_sum.test.cpp
test/yosupo/data_structure/dynamic_tree_vertex_add_path_sum.test.cpp -
test/yosupo/data_structure/persistent_range_affine_range_sum.test.cpp
test/yosupo/data_structure/persistent_range_affine_range_sum.test.cpp -
test/yosupo/data_structure/point_add_rectangle_sum.test.cpp
test/yosupo/data_structure/point_add_rectangle_sum.test.cpp -
test/yosupo/data_structure/point_set_range_composite.test.cpp
test/yosupo/data_structure/point_set_range_composite.test.cpp -
test/yosupo/data_structure/point_set_range_composite_large_array.test.cpp
test/yosupo/data_structure/point_set_range_composite_large_array.test.cpp -
test/yosupo/data_structure/point_set_range_frequency.test.cpp
test/yosupo/data_structure/point_set_range_frequency.test.cpp -
test/yosupo/data_structure/queue_operate_all_composite.test.cpp
test/yosupo/data_structure/queue_operate_all_composite.test.cpp -
test/yosupo/data_structure/range_affine_range_sum.test.cpp
test/yosupo/data_structure/range_affine_range_sum.test.cpp -
test/yosupo/data_structure/range_affine_range_sum_large_array.test.cpp
test/yosupo/data_structure/range_affine_range_sum_large_array.test.cpp -
test/yosupo/data_structure/range_reverse_range_sum.test.cpp
test/yosupo/data_structure/range_reverse_range_sum.test.cpp -
test/yosupo/data_structure/static_rmq.test.cpp
test/yosupo/data_structure/static_rmq.test.cpp -
test/yosupo/number_theory/discrete_logarithm_mod.test.cpp
test/yosupo/number_theory/discrete_logarithm_mod.test.cpp -
test/yosupo/tree/lowest_common_ancestor.test.cpp
test/yosupo/tree/lowest_common_ancestor.test.cpp -
test/yosupo/tree/tree_path_composite_sum.test.cpp
test/yosupo/tree/tree_path_composite_sum.test.cpp -
test/yosupo/tree/vertex_add_subtree_sum.test.cpp
test/yosupo/tree/vertex_add_subtree_sum.test.cpp -
test/yosupo/tree/vertex_set_path_composite.test.cpp
test/yosupo/tree/vertex_set_path_composite.test.cpp -
test/yukicoder/0261.test.cpp
test/yukicoder/0261.test.cpp -
test/yukicoder/0618.test.cpp
test/yukicoder/0618.test.cpp -
test/yukicoder/0901.test.cpp
test/yukicoder/0901.test.cpp -
test/yukicoder/1013.test.cpp
test/yukicoder/1013.test.cpp -
test/yukicoder/1036.test.cpp
test/yukicoder/1036.test.cpp -
test/yukicoder/1097.test.cpp
test/yukicoder/1097.test.cpp -
test/yukicoder/1242.test.cpp
test/yukicoder/1242.test.cpp -
test/yukicoder/1718.test.cpp
test/yukicoder/1718.test.cpp -
test/yukicoder/1790.test.cpp
test/yukicoder/1790.test.cpp -
test/yukicoder/1977.test.cpp
test/yukicoder/1977.test.cpp -
test/yukicoder/3327.test.cpp
test/yukicoder/3327.test.cpp
実装
#pragma once
#include <algorithm>
#include <concepts>
#include <limits>
#include <numeric>
#include <utility>
/// @brief モノイドの要件
/// @details 値型 `value_type`、単位元 `id()`、二項演算 `op()` を持つこと。
/// @tparam M 判定対象の型
/// @complexity コンパイル時制約で実行時計算量はない
template <class M>
concept monoid = requires {
typename M::value_type;
{ M::id() } -> std::same_as<typename M::value_type>;
{
M::op(std::declval<const typename M::value_type &>(), std::declval<const typename M::value_type &>())
} -> std::same_as<typename M::value_type>;
};
/// @brief 作用素モノイド M を値型 U に作用させられるか
/// @details モノイドに加えて作用 `f(作用素, 値)` を持つこと。遅延セグ木では `op` が作用素の合成、
/// `f` が値への作用を表す。`f` は関数テンプレートでも非テンプレートでもよい。
/// @tparam M 作用素モノイド
/// @tparam U 作用させる値の型
/// @complexity コンパイル時制約で実行時計算量はない
template <class M, class U>
concept acts_on = monoid<M> && requires(const typename M::value_type &m, const U &x) {
{ M::f(m, x) } -> std::convertible_to<U>;
};
/// @brief 和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Add {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 加法単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief lhs+rhsを返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs + rhs; }
/// @brief lhsをrhsへ加算して作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs + rhs;
}
};
/// @brief 積モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Mul {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 乗法単位元1を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(1); }
/// @brief `lhs * rhs` を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs * rhs; }
/// @brief lhsをrhsへ乗算して作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs * rhs;
}
};
/// @brief ビット積モノイド
/// @details 単位元は全ビットが 1 の値(符号付き型でも正しく動作する)。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct And {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 全bitが1の単位元を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return ~T(0); }
/// @brief bit積を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs & rhs; }
/// @brief bit積を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs & rhs;
}
};
/// @brief ビット和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Or {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief bit和を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs | rhs; }
/// @brief bit和を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs | rhs;
}
};
/// @brief ビット排他的論理和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Xor {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief bit排他的論理和を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs ^ rhs; }
/// @brief bit排他的論理和を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs ^ rhs;
}
};
/// @brief 最小値モノイド
/// @details 単位元は型の最大値。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Min {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 型の最大値を単位元として返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::max(); }
/// @brief 小さい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::min(lhs, rhs); }
/// @brief rhsをlhs以下へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::min(U(lhs), rhs);
}
};
/// @brief 最大値モノイド
/// @details 単位元は型の最小値。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Max {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 型の最小値を単位元として返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::lowest(); }
/// @brief 大きい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::max(lhs, rhs); }
/// @brief rhsをlhs以上へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::max(U(lhs), rhs);
}
};
/// @brief 最大値モノイド(単位元 0 固定)
/// @details 「何も選ばない」ことを単位元 0 として扱いたい場合に使う(`Max<T>` は単位元が型の最小値)。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct MaxZero {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief 大きい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::max(lhs, rhs); }
/// @brief rhsをlhs以上へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::max(U(lhs), rhs);
}
};
/// @brief 最大公約数モノイド
/// @details gcd(0, x) = x なので 0 が単位元。
/// @tparam T 要素の型
/// @complexity `id`は $O(1)$、`op`は $O(\log\min(lhs,rhs))$
template <class T>
struct Gcd {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief 最大公約数を返す
/// @complexity $O(\log\min(lhs,rhs))$
static constexpr T op(const T &lhs, const T &rhs) { return std::gcd(lhs, rhs); }
};
/// @brief 最小公倍数モノイド
/// @details lcm(1, x) = x なので 1 が単位元。
/// @tparam T 要素の型
/// @complexity `id`は $O(1)$、`op`は $O(\log\min(lhs,rhs))$
template <class T>
struct Lcm {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元1を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(1); }
/// @brief 最小公倍数を返す
/// @complexity $O(\log\min(lhs,rhs))$
static constexpr T op(const T &lhs, const T &rhs) { return std::lcm(lhs, rhs); }
};
/// @brief 上書きモノイド(後勝ち)
/// @details 「未設定」を表す番兵を単位元とする。値域にこの値を含めないこと。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Update {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 未設定を表す番兵を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::max(); }
/// @brief 新しい上書き値を優先して合成する
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs == id() ? rhs : lhs; }
/// @brief 未設定でなければlhsでrhsを上書きする
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs == id() ? rhs : U(lhs);
}
};
/// @brief アフィン変換モノイド
/// @details $f(x) = ax + b$ を `[a, b]` で表す。op(g, h) は「先に g、次に h を適用」した合成。
/// @tparam T 係数の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Affine {
/// @brief 係数pair型
/// @complexity 型エイリアスで実行時計算量はない
using P = std::pair<T, T>;
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = P;
/// @brief 恒等変換を返す
/// @complexity $O(1)$
static constexpr P id() { return P(T(1), T(0)); }
/// @brief 2つのアフィン変換を合成する
/// @complexity $O(1)$
static constexpr P op(const P &lhs, const P &rhs) {
return {lhs.first * rhs.first, rhs.first * lhs.second + rhs.second};
}
};
/// @brief モノイドの演算順序を反転するアダプタ
/// @tparam M 元となるモノイド
/// @complexity 元のモノイドの各演算と同じ
template <class M>
struct Rev {
/// @brief 元のモノイドの値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = typename M::value_type;
/// @brief 元のモノイドの単位元を返す
/// @complexity $M::id$ と同じ
static constexpr value_type id() { return M::id(); }
/// @brief 引数順を逆にして元の演算を適用する
/// @complexity $M::op$ と同じ
static constexpr value_type op(const value_type &lhs, const value_type &rhs) { return M::op(rhs, lhs); }
};
bundled
#line 2 "lib/segtree/monoid.hpp"
#include <algorithm>
#include <concepts>
#include <limits>
#include <numeric>
#include <utility>
/// @brief モノイドの要件
/// @details 値型 `value_type`、単位元 `id()`、二項演算 `op()` を持つこと。
/// @tparam M 判定対象の型
/// @complexity コンパイル時制約で実行時計算量はない
template <class M>
concept monoid = requires {
typename M::value_type;
{ M::id() } -> std::same_as<typename M::value_type>;
{
M::op(std::declval<const typename M::value_type &>(), std::declval<const typename M::value_type &>())
} -> std::same_as<typename M::value_type>;
};
/// @brief 作用素モノイド M を値型 U に作用させられるか
/// @details モノイドに加えて作用 `f(作用素, 値)` を持つこと。遅延セグ木では `op` が作用素の合成、
/// `f` が値への作用を表す。`f` は関数テンプレートでも非テンプレートでもよい。
/// @tparam M 作用素モノイド
/// @tparam U 作用させる値の型
/// @complexity コンパイル時制約で実行時計算量はない
template <class M, class U>
concept acts_on = monoid<M> && requires(const typename M::value_type &m, const U &x) {
{ M::f(m, x) } -> std::convertible_to<U>;
};
/// @brief 和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Add {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 加法単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief lhs+rhsを返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs + rhs; }
/// @brief lhsをrhsへ加算して作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs + rhs;
}
};
/// @brief 積モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Mul {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 乗法単位元1を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(1); }
/// @brief `lhs * rhs` を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs * rhs; }
/// @brief lhsをrhsへ乗算して作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs * rhs;
}
};
/// @brief ビット積モノイド
/// @details 単位元は全ビットが 1 の値(符号付き型でも正しく動作する)。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct And {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 全bitが1の単位元を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return ~T(0); }
/// @brief bit積を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs & rhs; }
/// @brief bit積を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs & rhs;
}
};
/// @brief ビット和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Or {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief bit和を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs | rhs; }
/// @brief bit和を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs | rhs;
}
};
/// @brief ビット排他的論理和モノイド
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Xor {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief bit排他的論理和を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs ^ rhs; }
/// @brief bit排他的論理和を作用する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs ^ rhs;
}
};
/// @brief 最小値モノイド
/// @details 単位元は型の最大値。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Min {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 型の最大値を単位元として返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::max(); }
/// @brief 小さい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::min(lhs, rhs); }
/// @brief rhsをlhs以下へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::min(U(lhs), rhs);
}
};
/// @brief 最大値モノイド
/// @details 単位元は型の最小値。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Max {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 型の最小値を単位元として返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::lowest(); }
/// @brief 大きい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::max(lhs, rhs); }
/// @brief rhsをlhs以上へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::max(U(lhs), rhs);
}
};
/// @brief 最大値モノイド(単位元 0 固定)
/// @details 「何も選ばない」ことを単位元 0 として扱いたい場合に使う(`Max<T>` は単位元が型の最小値)。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct MaxZero {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief 大きい方を返す
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return std::max(lhs, rhs); }
/// @brief rhsをlhs以上へ制限する
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return std::max(U(lhs), rhs);
}
};
/// @brief 最大公約数モノイド
/// @details gcd(0, x) = x なので 0 が単位元。
/// @tparam T 要素の型
/// @complexity `id`は $O(1)$、`op`は $O(\log\min(lhs,rhs))$
template <class T>
struct Gcd {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元0を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(0); }
/// @brief 最大公約数を返す
/// @complexity $O(\log\min(lhs,rhs))$
static constexpr T op(const T &lhs, const T &rhs) { return std::gcd(lhs, rhs); }
};
/// @brief 最小公倍数モノイド
/// @details lcm(1, x) = x なので 1 が単位元。
/// @tparam T 要素の型
/// @complexity `id`は $O(1)$、`op`は $O(\log\min(lhs,rhs))$
template <class T>
struct Lcm {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 単位元1を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return T(1); }
/// @brief 最小公倍数を返す
/// @complexity $O(\log\min(lhs,rhs))$
static constexpr T op(const T &lhs, const T &rhs) { return std::lcm(lhs, rhs); }
};
/// @brief 上書きモノイド(後勝ち)
/// @details 「未設定」を表す番兵を単位元とする。値域にこの値を含めないこと。
/// @tparam T 要素の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Update {
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = T;
/// @brief 未設定を表す番兵を返す
/// @complexity $O(1)$
static constexpr T id() noexcept { return std::numeric_limits<T>::max(); }
/// @brief 新しい上書き値を優先して合成する
/// @complexity $O(1)$
static constexpr T op(const T &lhs, const T &rhs) noexcept { return lhs == id() ? rhs : lhs; }
/// @brief 未設定でなければlhsでrhsを上書きする
/// @complexity $O(1)$
template <class U>
static constexpr U f(const T &lhs, const U &rhs) noexcept {
return lhs == id() ? rhs : U(lhs);
}
};
/// @brief アフィン変換モノイド
/// @details $f(x) = ax + b$ を `[a, b]` で表す。op(g, h) は「先に g、次に h を適用」した合成。
/// @tparam T 係数の型
/// @complexity 各演算 $O(1)$
template <class T>
struct Affine {
/// @brief 係数pair型
/// @complexity 型エイリアスで実行時計算量はない
using P = std::pair<T, T>;
/// @brief 値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = P;
/// @brief 恒等変換を返す
/// @complexity $O(1)$
static constexpr P id() { return P(T(1), T(0)); }
/// @brief 2つのアフィン変換を合成する
/// @complexity $O(1)$
static constexpr P op(const P &lhs, const P &rhs) {
return {lhs.first * rhs.first, rhs.first * lhs.second + rhs.second};
}
};
/// @brief モノイドの演算順序を反転するアダプタ
/// @tparam M 元となるモノイド
/// @complexity 元のモノイドの各演算と同じ
template <class M>
struct Rev {
/// @brief 元のモノイドの値型
/// @complexity 型エイリアスで実行時計算量はない
using value_type = typename M::value_type;
/// @brief 元のモノイドの単位元を返す
/// @complexity $M::id$ と同じ
static constexpr value_type id() { return M::id(); }
/// @brief 引数順を逆にして元の演算を適用する
/// @complexity $M::op$ と同じ
static constexpr value_type op(const value_type &lhs, const value_type &rhs) { return M::op(rhs, lhs); }
};