diff --git a/.verify-helper/timestamps.remote.json b/.verify-helper/timestamps.remote.json index e3dcf058..2d3a3bf9 100644 --- a/.verify-helper/timestamps.remote.json +++ b/.verify-helper/timestamps.remote.json @@ -21,7 +21,8 @@ "combinatorial_opt/test/simplex.multiprecision.test.cpp": "2021-02-28 16:53:36 +0900", "combinatorial_opt/test/simplex.shortestpath.test.cpp": "2021-02-28 16:53:36 +0900", "convex_hull_trick/test/convex_hull_trick.test.cpp": "2020-11-18 20:33:55 +0900", -"convex_hull_trick/test/li_chao_tree.test.cpp": "2021-09-16 00:36:41 +0900", +"convex_hull_trick/test/li_chao_tree.test.cpp": "2021-09-20 19:53:17 +0900", +"convex_hull_trick/test/monotone-insert-get_cht.test.cpp": "2021-09-20 20:03:18 +0900", "convolution/test/bitwise_and_conv.test.cpp": "2021-06-06 14:54:00 +0900", "convolution/test/bitwise_xor_conv.test.cpp": "2021-06-06 14:54:00 +0900", "convolution/test/hadamard_xor.test.cpp": "2020-12-20 04:05:21 +0900", diff --git a/convex_hull_trick/add-get-monotone_cht.hpp b/convex_hull_trick/add-get-monotone_cht.hpp deleted file mode 100644 index f27d6067..00000000 --- a/convex_hull_trick/add-get-monotone_cht.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include <cassert> -#include <list> -#include <utility> - -// CUT begin -// Convex Hull Trick for monotone increasing queries, monotone decreasing slopes -// Each operation is amortized O(1) -// - is_minimizer: if true, calculates min. Otherwise, calculates max. -// - add_line(a, b): Add `y = ax + b`, a must be monotone decreasing (if is_minimizer == true) / increasing (otherwise) -// - add_convex_parabola(c, a, b): Add `y = c(x - a)^2 + b`, c is constant, a is monotone increasing (if is_minimizer == true) / decreasing (otherwise) -// - get(x): Calculate min/max. value of `y = ax + b`'s at point x, x must be monotone increasing FOR BOTH CASES. -// - parabola_get(c, x): Caclculate min/max. value of `y = c(x - a)^2 + b`'s, x must be monotone increasing FOR BOTH CASES. -// - If you need random access, change `std::list` to `std::deque` -// Verified: <https://yukicoder.me/submissions/409156> -template <bool is_minimizer, typename T_CHT = long long, typename T_MP = __int128, T_CHT INF = 1LL << 61> -struct MonotoneConvexHullTrick : std::list<std::pair<T_CHT, T_CHT>> // (a, b) means `y = ax + b` -{ - MonotoneConvexHullTrick() = default; - void add_line(T_CHT a, T_CHT b) { // Add y = ax + b - if (!is_minimizer) a = -a, b = -b; - assert(this->empty() or this->back().first >= a); - while (this->size() > 1u) { - if (this->back().first == a) { - if (this->back().second <= b) { - return; - } else { - this->pop_back(); - continue; - } - } - auto ill = std::prev(this->end(), 2); - auto l = (T_MP)(this->back().second - ill->second) * (this->back().first - a); // Overflow might occur here. - auto r = (T_MP)(b - this->back().second) * (ill->first - this->back().first); - if (l < r) break; - this->pop_back(); - } - this->emplace_back(a, b); - } - T_CHT get(T_CHT x) { - while (this->size() > 1u and - this->begin()->first * x + this->begin()->second >= (++this->begin())->first * x + (++this->begin())->second) - this->pop_front(); - return (this->empty() ? INF : this->begin()->first * x + this->begin()->second) * (is_minimizer ? 1 : -1); - } - void add_convex_parabola(T_CHT c, T_CHT a, T_CHT b) { add_line(c * a * (-2), c * a * a + b); } - T_CHT parabola_get(T_CHT c, T_CHT x) { return get(x) + c * x * x; } - - static MonotoneConvexHullTrick merge(const MonotoneConvexHullTrick &cht1, const MonotoneConvexHullTrick &cht2) { - MonotoneConvexHullTrick ret; - auto i1 = cht1.begin(), i2 = cht2.begin(); - static const T_CHT sgn = is_minimizer ? 1 : -1; - T_CHT a = 0, b = 0; - while (i1 != cht1.end() and i2 != cht2.end()) { - if (i1->first == i2->first) { - a = i1->first, b = std::min(i1->second, i2->second); - i1++, i2++; - } else if (i1->first > i2->first) { - a = i1->first, b = i1->second, i1++; - } else { - a = i2->first, b = i2->second, i2++; - } - ret.add_line(a * sgn, b * sgn); - } - while (i1 != cht1.end()) ret.add_line(i1->first * sgn, i1->second * sgn), i1++; - while (i2 != cht2.end()) ret.add_line(i2->first * sgn, i2->second * sgn), i2++; - return ret; - } -}; diff --git a/convex_hull_trick/li_chao_tree.hpp b/convex_hull_trick/li_chao_tree.hpp index e116dbbe..19a20709 100644 --- a/convex_hull_trick/li_chao_tree.hpp +++ b/convex_hull_trick/li_chao_tree.hpp @@ -6,7 +6,7 @@ // Li-Chao tree // init() : set x's where we will execute get(x) queries -// add_segment(l, r, a, b): update by ax + b in [l, r) +// insert_segment(l, r, a, b): update by ax + b in [l, r) // get(x): get min template <class T, class T_MP> struct li_chao_tree { int _n, _head; @@ -78,12 +78,12 @@ template <class T, class T_MP> struct li_chao_tree { } } - void add_line(T a, T b, int idx = -1) { + void insert_line(T a, T b, int idx = -1) { il = 0, ir = _n; if (il >= ir) return; _rec(1, 0, _head, _Line{a, b, idx, true}); } - void add_segment(T xl, T xr, T a, T b, int idx = -1) { + void insert_segment(T xl, T xr, T a, T b, int idx = -1) { il = std::lower_bound(xs.begin(), xs.end(), xl) - xs.begin(); ir = std::lower_bound(xs.begin(), xs.end(), xr) - xs.begin(); if (il >= ir) return; diff --git a/convex_hull_trick/li_chao_tree.md b/convex_hull_trick/li_chao_tree.md index fad0a37b..b156ed37 100644 --- a/convex_hull_trick/li_chao_tree.md +++ b/convex_hull_trick/li_chao_tree.md @@ -13,7 +13,7 @@ vector<long long> xs; li_chao_tree<long long, __int128> tree; tree.init(xs); // xs が昇順である必要はない -tree.add_segment(l, r, a, b, 0); // chmin by ax + b in [l, r) +tree.insert_segment(l, r, a, b, 0); // chmin by ax + b in [l, r) ret = tree.get(x); cout << ret.minval << endl; diff --git a/convex_hull_trick/monotone-insert-get-cht.md b/convex_hull_trick/monotone-insert-get-cht.md new file mode 100644 index 00000000..b0d6ce0e --- /dev/null +++ b/convex_hull_trick/monotone-insert-get-cht.md @@ -0,0 +1,17 @@ +--- +title: Convex hull trick (monotone insert, monotone get) +documentation_of: ./monotone-insert-get_cht.hpp +--- + +各クエリが償却 $O(1)$. + +- `insert_line(T a, T b)` 直線追加クエリ.追加される直線 $y = ax + b$ について,$a$ の値が単調非増加(最小値取得) / 単調非減少(最大値取得). +- `get(T x)` 最小値・最大値取得クエリ.取得する $x$ 座標は単調非減少. + +- `insert_convex_parabola(T c, T a, T b)` 放物線 $y = c(x - a)^2 + b$ の追加クエリ.$c$ の値は全クエリで共通でなければならない.最小値取得の場合,$ca$ の値が単調増加でなければならない. +- `parabola_get(T c, T x)` 放物線たちの最小値・最大値取得クエリ.取得する $x$ 座標は単調非減少. +- `merge(CHT cht1, CHT cht2)` 二つの CHT をマージする.計算量は $O(\mathrm{size}(\mathrm{cht1}) + \mathrm{size}(\mathrm{cht2}))$. + +## 問題例 + +- [No.952 危険な火薬庫 - yukicoder](https://yukicoder.me/problems/no/952) diff --git a/convex_hull_trick/monotone-insert-get_cht.hpp b/convex_hull_trick/monotone-insert-get_cht.hpp new file mode 100644 index 00000000..ba8f5f9f --- /dev/null +++ b/convex_hull_trick/monotone-insert-get_cht.hpp @@ -0,0 +1,85 @@ +#pragma once +#include <cassert> +#include <list> +#include <utility> + +// CUT begin +// Convex Hull Trick for monotone increasing queries, monotone decreasing slopes +// Each operation is amortized O(1) +// - is_minimizer: if true, calculates min. Otherwise, calculates max. +// - insert_line(a, b): Insert `y = ax + b`, +// a must be monotone decreasing (if is_minimizer == true) / increasing (otherwise) +// - add_convex_parabola(c, a, b): Add `y = c(x - a)^2 + b`, c is constant, a is monotone +// increasing (if is_minimizer == true) / decreasing (otherwise) +// - get(x): Calculate min/max. value of `y = ax + b`'s at point x, x must be monotone +// increasing FOR BOTH CASES. +// - parabola_get(c, x): Caclculate min/max. value of `y = c(x - a)^2 + b`'s, x must be monotone +// increasing FOR BOTH CASES. +// - If you need random access, change `std::list` to `std::deque` +// Verified: https://yukicoder.me/submissions/409156 +template <bool is_minimizer, class T = long long, class T_MP = __int128, T INF = 1LL << 61> +class MonotoneConvexHullTrick : std::list<std::pair<T, T>> { + // (a, b) means `y = ax + b` + T_MP _eval(typename std::list<std::pair<T, T>>::const_iterator itr, T x) { + return T_MP(itr->first) * x + itr->second; + } + +public: + MonotoneConvexHullTrick() { static_assert(INF > 0, "INF must be positive."); } + void insert_line(T a, T b) { // Add y = ax + b + if (!is_minimizer) a = -a, b = -b; + assert(this->empty() or this->back().first >= a); + while (this->size() > 1u) { + if (this->back().first == a) { + if (this->back().second <= b) return; + this->pop_back(); + continue; + } + auto ill = std::prev(this->end(), 2); + auto l = (T_MP)(this->back().second - ill->second) * (this->back().first - a); + auto r = (T_MP)(b - this->back().second) * (ill->first - this->back().first); + if (l < r) break; + this->pop_back(); + } + this->emplace_back(a, b); + } + + struct Ret { + T line_a, line_b; + bool is_valid; + T_MP val; + }; + Ret get(T x) { + if (this->empty()) return {0, 0, false, is_minimizer ? INF : -INF}; + while (this->size() > 1 and _eval(this->begin(), x) >= _eval(std::next(this->begin()), x)) { + this->pop_front(); + } + T_MP val = _eval(this->begin(), x) * (is_minimizer ? 1 : -1); + return {(is_minimizer ? 1 : -1) * this->begin()->first, + (is_minimizer ? 1 : -1) * this->begin()->second, true, val}; + } + void insert_convex_parabola(T c, T a, T b) { insert_line(c * a * (-2), c * a * a + b); } + T_MP parabola_get(T c, T x) { return get(x).val + c * x * x; } + + static MonotoneConvexHullTrick + merge(const MonotoneConvexHullTrick &cht1, const MonotoneConvexHullTrick &cht2) { + MonotoneConvexHullTrick ret; + auto i1 = cht1.begin(), i2 = cht2.begin(); + static const T sgn = is_minimizer ? 1 : -1; + T a = 0, b = 0; + while (i1 != cht1.end() and i2 != cht2.end()) { + if (i1->first == i2->first) { + a = i1->first, b = std::min(i1->second, i2->second); + ++i1, ++i2; + } else if (i1->first > i2->first) { + a = i1->first, b = i1->second, ++i1; + } else { + a = i2->first, b = i2->second, ++i2; + } + ret.insert_line(a * sgn, b * sgn); + } + while (i1 != cht1.end()) ret.insert_line(i1->first * sgn, i1->second * sgn), ++i1; + while (i2 != cht2.end()) ret.insert_line(i2->first * sgn, i2->second * sgn), ++i2; + return ret; + } +}; diff --git a/convex_hull_trick/test/li_chao_tree.test.cpp b/convex_hull_trick/test/li_chao_tree.test.cpp index f58eb96e..b16ae291 100644 --- a/convex_hull_trick/test/li_chao_tree.test.cpp +++ b/convex_hull_trick/test/li_chao_tree.test.cpp @@ -36,7 +36,7 @@ int main() { for (auto q : qs) { tie(tp, l, r, a, b) = q; - if (tp == 0) tree.add_segment(l, r, a, b, 0); + if (tp == 0) tree.insert_segment(l, r, a, b, 0); if (tp == 1) { auto ret = tree.get(l); if (ret.is_valid) { diff --git a/convex_hull_trick/test/monotone-insert-get_cht.test.cpp b/convex_hull_trick/test/monotone-insert-get_cht.test.cpp new file mode 100644 index 00000000..c78cf154 --- /dev/null +++ b/convex_hull_trick/test/monotone-insert-get_cht.test.cpp @@ -0,0 +1,27 @@ +#define PROBLEM "https://yukicoder.me/problems/no/952" +#include "../monotone-insert-get_cht.hpp" +#include <iostream> +#include <vector> +using namespace std; + +int main() { + int N; + cin >> N; + vector<long long> A(N); + for (auto &x : A) cin >> x; + vector<MonotoneConvexHullTrick<true, long long, __int128>> cht(N + 1); + int x = 0; + cht[0].insert_convex_parabola(1, x, 0); + for (int i = 0; i < N; ++i) { + for (int d = i; d >= 0; --d) { + long long v = cht[d].parabola_get(1, x); + cht[d + 1].insert_convex_parabola(1, x + A[i], v); + } + x += A[i]; + } + cht.pop_back(); + while (!cht.empty()) { + cout << (long long)cht.back().parabola_get(1, x) << '\n'; + cht.pop_back(); + } +}