"Geometry being useful", I copied that down from this blog. And yes, in any subject, geometry often steps in to offer a fresh perspective. The Convex Hull Trick is a testament to this, seamlessly blending algorithmic efficiency with geometric intuition. In this post, I will discuss pretty much about this method.
Motivational Problem
Suppose there are KaTeX can only parse string typed expression
n rectangles in a 2D Cartesian Plane, each defined by vertices KaTeX can only parse string typed expression
(0,0) and KaTeX can only parse string typed expression
(xi,yi), along with a number KaTeX can only parse string typed expression
ai. We want to select some of these rectangles such that the area of their union minus the sum of the KaTeX can only parse string typed expression
ai values of the chosen rectangles is maximized.
Constraints given:
KaTeX can only parse string typed expression
1≤n≤106
KaTeX can only parse string typed expression
1≤xi,yi≤109 (for each KaTeX can only parse string typed expression
i such that KaTeX can only parse string typed expression
0≤i<n)
KaTeX can only parse string typed expression
0≤ai≤xi⋅yi (for each KaTeX can only parse string typed expression
i such that KaTeX can only parse string typed expression
0≤i<n)
- No nested rectangles exist
This problem comes straight from Codeforces Round 526 (Div. 1E).
Dynamic Programming Solution
Let's consider these KaTeX can only parse string typed expression
(xi,yi,ai) tuples: KaTeX can only parse string typed expression
[(6,2,4),(1,6,2),(2,4,3),(5,3,8)]].
Because no nested rectangles exist in all cases, we can sort these tuples by KaTeX can only parse string typed expression
x without losing their essences. Note that sorting by KaTeX can only parse string typed expression
x in ascending order also sorts the tuples by KaTeX can only parse string typed expression
y in descending order.
Denote KaTeX can only parse string typed expression
dpi as the maximum value that can be achieved by selecting the KaTeX can only parse string typed expression
i-th rectangle (from the sorted version) and some of the previous rectangles. Then, our answer will be
KaTeX can only parse string typed expression
ans=0≤i<nmaxdpi
, where
KaTeX can only parse string typed expression
dpi=(xi⋅yi−Ai)+max(0≤j<imaxdpj−xj⋅yi, 0)
.
Parts explanation:
- The
KaTeX can only parse string typed expression
(xi⋅yi−Ai) part is obtained as the reward for selecting the KaTeX can only parse string typed expression
i-th rectangle.
- The
KaTeX can only parse string typed expression
dpj part acts as an additional reward for selecting the KaTeX can only parse string typed expression
j-th rectangle.
- The
KaTeX can only parse string typed expression
−xj⋅yi part, which is derived from KaTeX can only parse string typed expression
−min(xi,xj)⋅min(yi,yj), is obtained as the consequence of selecting the KaTeX can only parse string typed expression
i-th rectangle with KaTeX can only parse string typed expression
j-th rectangle (and presumably any rectangle that maximizes the KaTeX can only parse string typed expression
dpj value). Note that KaTeX can only parse string typed expression
xj⋅yi is the intersection for the KaTeX can only parse string typed expression
i-th rectangle and the KaTeX can only parse string typed expression
j-rectangle. This part is important as the union area of the KaTeX can only parse string typed expression
i-th rectangle and the KaTeX can only parse string typed expression
j-th rectangle is KaTeX can only parse string typed expression
(xi⋅yi) ∪ (xj⋅yj)=(xi⋅yi)+(xj⋅yj)−(xi⋅yi) ∩ (xj⋅yj).
This dynamic programming solution works in KaTeX can only parse string typed expression
O(n2); however, it is still not suffice as KaTeX can only parse string typed expression
n might be KaTeX can only parse string typed expression
106. We need an optimization towards it.
Convex Hull Trick
This is where our main optimization technique, the Convex Hull Trick, comes in handy. The Convex Hull Trick is a computational geometry used to manage a set of affine functions, constructing a lower / upper hull. To illustrate, suppose we are going to insert the following affine functions: KaTeX can only parse string typed expression
−x+4, KaTeX can only parse string typed expression
−2x+5, KaTeX can only parse string typed expression
−5x+8, and KaTeX can only parse string typed expression
−6x+10, into our lower hull.
Notice that in our lower hull, we always want to have the intersection points of every two lines that are side by side sorted. Formally, if we have a set of affine functions in our lower hull, say KaTeX can only parse string typed expression
f0(x), KaTeX can only parse string typed expression
f1(x), KaTeX can only parse string typed expression
…, and KaTeX can only parse string typed expression
fn−1(x), with KaTeX can only parse string typed expression
pi representing the x-coordinate of the intersection point of KaTeX can only parse string typed expression
fi(x) and KaTeX can only parse string typed expression
fi+1(x), i.e., KaTeX can only parse string typed expression
fi(pi)=fi+1(pi), for KaTeX can only parse string typed expression
0≤i<n−2; then, it is essential that KaTeX can only parse string typed expression
p0>p1>…>pn−2 (or sorted in the other way).
If there is a scenario where KaTeX can only parse string typed expression
pi=pi+1, we can discard either KaTeX can only parse string typed expression
fi(x) or KaTeX can only parse string typed expression
fi+1(x). The choice depends on the gradient, considering whether it is lower / higher and negative / positive.
The implementation is as follows:
struct Line {
long long m, c;
Line(long long m, long long c) : m(m), c(c) {}
long long y(long long x) { return m * x + c; }
double intersect(Line other) const {
return (double)(c - other.c) / (double)(other.m - m);
}
};
vector<Line> ch;
void addLine(Line line) {
auto comp = [&](const Line &a, const Line &b, const Line &c) -> bool {
return a.intersect(b) > b.intersect(c); // p_0 > p_1 > ... > p_{n - 2}
};
while (ch.size() >= 2 && !comp(ch[ch.size() - 2], ch.back(), line)) {
ch.pop_back();
}
ch.push_back(line);
}
The implementation above operates efficiently when the inserted gradient is in descending order. The reason will be explained later.
In our set of affine functions, it can be observed that:
KaTeX can only parse string typed expression
f0(x) maximizes the value of KaTeX can only parse string typed expression
x for KaTeX can only parse string typed expression
x∈[p0,∞);
KaTeX can only parse string typed expression
f1(x) maximizes the value of KaTeX can only parse string typed expression
x for KaTeX can only parse string typed expression
x∈[p1,p0];
KaTeX can only parse string typed expression
…
KaTeX can only parse string typed expression
fn−2(x) maximizes the value of KaTeX can only parse string typed expression
x for KaTeX can only parse string typed expression
x∈[pn−2,pn−3];
KaTeX can only parse string typed expression
fn−1(x) maximizes the value of KaTeX can only parse string typed expression
x for KaTeX can only parse string typed expression
x∈(−∞,pn−2].
Using this observation, whenever we want to look up for KaTeX can only parse string typed expression
i that maximizes KaTeX can only parse string typed expression
fi(x) for an KaTeX can only parse string typed expression
x, we can perform a binary search on the intersection points to determine the domain where KaTeX can only parse string typed expression
x lies.
int optimalLine(long long x) {
auto p = [&](const int i) -> double { return ch[i].intersect(ch[i + 1]); };
return lower_bound(ch.begin(), ch.end() - 1, x,
[&](const Line &line, long long val) {
int idx = &line - &ch[0];
return p(idx) > val;
}) -
ch.begin();
}
Reverting to our primary dynamic programming solution, where we had:
KaTeX can only parse string typed expression
dpi=(xi⋅yi−Ai)+max(0≤j<imaxdpj−xj⋅yi, 0)
Regardless of the value of KaTeX can only parse string typed expression
j, we consistently add KaTeX can only parse string typed expression
xi⋅yi−Ai to KaTeX can only parse string typed expression
dpi; thus, this can be omitted from our optimization process. An affine function KaTeX can only parse string typed expression
fi(x)=mi⋅x+ci is formed by assigning KaTeX can only parse string typed expression
mi as KaTeX can only parse string typed expression
−xi and KaTeX can only parse string typed expression
ci as KaTeX can only parse string typed expression
dpi. If KaTeX can only parse string typed expression
C represents a constant in KaTeX can only parse string typed expression
dpi, then KaTeX can only parse string typed expression
C=xi⋅yi−Ai. Our KaTeX can only parse string typed expression
dp equation can then be expressed as:
KaTeX can only parse string typed expression
dpi=max(0≤j<imax(mj⋅yi+cj), 0)+C
To further simplify our dynamic programming, we may also add a line KaTeX can only parse string typed expression
f−1(x)=0 to handle the KaTeX can only parse string typed expression
max(…,0) component.
KaTeX can only parse string typed expression
dpi=−1≤j<imax(mj⋅yi+cj)+C
The solution's flow is as follows:
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
struct InputFormat {
long long x, y, A;
bool operator<(const InputFormat &other) const {
return tie(x, y, A) < tie(other.x, other.y, other.A);
}
};
long long N, ans = 0;
cin >> N;
vector<InputFormat> arr(N);
vector<long long> x(N), y(N), A(N), dp(N);
for (auto &a : arr)
cin >> a.x >> a.y >> a.A;
sort(arr.begin(), arr.end());
for (int i = 0; i < N; ++i)
tie(x[i], y[i], A[i]) = make_tuple(arr[i].x, arr[i].y, arr[i].A);
addLine(Line(0, 0));
for (int i = 0; i < N; ++i) {
dp[i] = ch[optimalLine(y[i])].y(y[i]) + (x[i] * y[i] - A[i]);
addLine(Line(-x[i], dp[i]));
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}
The gradient of the inserted line is guaranteed to be in descending order as the gradient is KaTeX can only parse string typed expression
−xi and KaTeX can only parse string typed expression
xi is in ascending order. This explains the previous KaTeX can only parse string typed expression
addLine implementation. Querying the KaTeX can only parse string typed expression
optimalLine only performs binary search from KaTeX can only parse string typed expression
0 to KaTeX can only parse string typed expression
n−1, resulting in a complexity of KaTeX can only parse string typed expression
O(logn) per query. The armortized total operations in KaTeX can only parse string typed expression
addLine function is KaTeX can only parse string typed expression
O(n). The overall solution operates in KaTeX can only parse string typed expression
O(nlogn). My full solution is accessible here.
Variation
Upper Hull
In a case where we have a minimization problem, we want to look up for the upper hull. Some comparisons in the code, especially in KaTeX can only parse string typed expression
comp within KaTeX can only parse string typed expression
addLine and the binary search in KaTeX can only parse string typed expression
optimalLine, need to be inverted.
Dynamic Convex Hull Trick
In this particular case, we do not have the guarantee of having the gradient insertion sorted, whether it is in ascending order or in descending order; consequently, we no longer can use KaTeX can only parse string typed expression
std::vector for our data structure. We may use KaTeX can only parse string typed expression
std::set to maintain the dynamic gradients. Or... we may use Li Chao Tree, which I will discuss in the upcoming post.
And there we have it! I have always found the Convex Hull Trick fascinating, especially when it comes to refine a naive KaTeX can only parse string typed expression
O(n2) dynamic programming approach down to KaTeX can only parse string typed expression
O(nlogn). Of course, it still requires an additional property, which is to have the dynamic programming rewritable as an affine function. Some other DP optimizations, such as Divide and Conquer, Knuth, and more will probably discussed in the future posts as well! Stay tuned!
© 2023 Yohandi. All rights reserved.