Skip to content

Commit 8a2a1ac

Browse files
committed
Refactor & add test for monotone-insert-get CHT
1 parent e75dba6 commit 8a2a1ac

File tree

4 files changed

+129
-69
lines changed

4 files changed

+129
-69
lines changed

convex_hull_trick/add-get-monotone_cht.hpp

-69
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: Convex hull trick (monotone insert, monotone get)
3+
documentation_of: ./monotone-insert-get_cht.hpp
4+
---
5+
6+
各クエリが償却 $O(1)$.
7+
8+
- `insert_line(T a, T b)` 直線追加クエリ.追加される直線 $y = ax + b$ について,$a$ の値が単調非増加(最小値取得) / 単調非減少(最大値取得).
9+
- `get(T x)` 最小値・最大値取得クエリ.取得する $x$ 座標は単調非減少.
10+
11+
- `insert_convex_parabola(T c, T a, T b)` 放物線 $y = c(x - a)^2 + b$ の追加クエリ.$c$ の値は全クエリで共通でなければならない.最小値取得の場合,$ca$ の値が単調増加でなければならない.
12+
- `parabola_get(T c, T x)` 放物線たちの最小値・最大値取得クエリ.取得する $x$ 座標は単調非減少.
13+
- `merge(CHT cht1, CHT cht2)` 二つの CHT をマージする.計算量は $O(\mathrm{size}(\mathrm{cht1}) + \mathrm{size}(\mathrm{cht2}))$.
14+
15+
## 問題例
16+
17+
- [No.952 危険な火薬庫 - yukicoder](https://yukicoder.me/problems/no/952)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <list>
4+
#include <utility>
5+
6+
// CUT begin
7+
// Convex Hull Trick for monotone increasing queries, monotone decreasing slopes
8+
// Each operation is amortized O(1)
9+
// - is_minimizer: if true, calculates min. Otherwise, calculates max.
10+
// - insert_line(a, b): Insert `y = ax + b`,
11+
// a must be monotone decreasing (if is_minimizer == true) / increasing (otherwise)
12+
// - add_convex_parabola(c, a, b): Add `y = c(x - a)^2 + b`, c is constant, a is monotone
13+
// increasing (if is_minimizer == true) / decreasing (otherwise)
14+
// - get(x): Calculate min/max. value of `y = ax + b`'s at point x, x must be monotone
15+
// increasing FOR BOTH CASES.
16+
// - parabola_get(c, x): Caclculate min/max. value of `y = c(x - a)^2 + b`'s, x must be monotone
17+
// increasing FOR BOTH CASES.
18+
// - If you need random access, change `std::list` to `std::deque`
19+
// Verified: https://yukicoder.me/submissions/409156
20+
template <bool is_minimizer, class T = long long, class T_MP = __int128, T INF = 1LL << 61>
21+
class MonotoneConvexHullTrick : std::list<std::pair<T, T>> {
22+
// (a, b) means `y = ax + b`
23+
T_MP _eval(typename std::list<std::pair<T, T>>::const_iterator itr, T x) {
24+
return T_MP(itr->first) * x + itr->second;
25+
}
26+
27+
public:
28+
MonotoneConvexHullTrick() { static_assert(INF > 0, "INF must be positive."); }
29+
void insert_line(T a, T b) { // Add y = ax + b
30+
if (!is_minimizer) a = -a, b = -b;
31+
assert(this->empty() or this->back().first >= a);
32+
while (this->size() > 1u) {
33+
if (this->back().first == a) {
34+
if (this->back().second <= b) return;
35+
this->pop_back();
36+
continue;
37+
}
38+
auto ill = std::prev(this->end(), 2);
39+
auto l = (T_MP)(this->back().second - ill->second) * (this->back().first - a);
40+
auto r = (T_MP)(b - this->back().second) * (ill->first - this->back().first);
41+
if (l < r) break;
42+
this->pop_back();
43+
}
44+
this->emplace_back(a, b);
45+
}
46+
47+
struct Ret {
48+
T line_a, line_b;
49+
bool is_valid;
50+
T_MP val;
51+
};
52+
Ret get(T x) {
53+
if (this->empty()) return {0, 0, false, is_minimizer ? INF : -INF};
54+
while (this->size() > 1 and _eval(this->begin(), x) >= _eval(std::next(this->begin()), x)) {
55+
this->pop_front();
56+
}
57+
T_MP val = _eval(this->begin(), x) * (is_minimizer ? 1 : -1);
58+
return {(is_minimizer ? 1 : -1) * this->begin()->first,
59+
(is_minimizer ? 1 : -1) * this->begin()->second, true, val};
60+
}
61+
void insert_convex_parabola(T c, T a, T b) { insert_line(c * a * (-2), c * a * a + b); }
62+
T_MP parabola_get(T c, T x) { return get(x).val + c * x * x; }
63+
64+
static MonotoneConvexHullTrick
65+
merge(const MonotoneConvexHullTrick &cht1, const MonotoneConvexHullTrick &cht2) {
66+
MonotoneConvexHullTrick ret;
67+
auto i1 = cht1.begin(), i2 = cht2.begin();
68+
static const T sgn = is_minimizer ? 1 : -1;
69+
T a = 0, b = 0;
70+
while (i1 != cht1.end() and i2 != cht2.end()) {
71+
if (i1->first == i2->first) {
72+
a = i1->first, b = std::min(i1->second, i2->second);
73+
++i1, ++i2;
74+
} else if (i1->first > i2->first) {
75+
a = i1->first, b = i1->second, ++i1;
76+
} else {
77+
a = i2->first, b = i2->second, ++i2;
78+
}
79+
ret.insert_line(a * sgn, b * sgn);
80+
}
81+
while (i1 != cht1.end()) ret.insert_line(i1->first * sgn, i1->second * sgn), ++i1;
82+
while (i2 != cht2.end()) ret.insert_line(i2->first * sgn, i2->second * sgn), ++i2;
83+
return ret;
84+
}
85+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#define PROBLEM "https://yukicoder.me/problems/no/952"
2+
#include "../monotone-insert-get_cht.hpp"
3+
#include <iostream>
4+
#include <vector>
5+
using namespace std;
6+
7+
int main() {
8+
int N;
9+
cin >> N;
10+
vector<long long> A(N);
11+
for (auto &x : A) cin >> x;
12+
vector<MonotoneConvexHullTrick<true, long long, __int128>> cht(N + 1);
13+
int x = 0;
14+
cht[0].insert_convex_parabola(1, x, 0);
15+
for (int i = 0; i < N; ++i) {
16+
for (int d = i; d >= 0; --d) {
17+
long long v = cht[d].parabola_get(1, x);
18+
cht[d + 1].insert_convex_parabola(1, x + A[i], v);
19+
}
20+
x += A[i];
21+
}
22+
cht.pop_back();
23+
while (!cht.empty()) {
24+
cout << (long long)cht.back().parabola_get(1, x) << '\n';
25+
cht.pop_back();
26+
}
27+
}

0 commit comments

Comments
 (0)