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

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
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)
Lines changed: 85 additions & 0 deletions
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+
};
Lines changed: 27 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy