7.5 KiB
id | title | author | prerequisites | ||
---|---|---|---|---|---|
slope | Slope Trick | Benjamin Qi |
|
Slope trick refers to manipulating piecewise linear convex functions. Includes a simple solution to Landscaping.
Tutorials
From the latter link (modified):
Slope trick is a way to represent a function that satisfies the following conditions:
- It can be divided into multiple sections, where each section is a linear function (usually) with an integer slope.
- It is a convex/concave function. In other words, the slope of each section is non-decreasing or non-increasing when scanning the function from left to right.
It's generally applicable as a DP optimization. Usually you can come up with a slower DP (ex. O(N^2)
) first and then optimize it to O(N\log N)
with slope trick.
Buy Low Sell High
Slow Solution: Let dp[i][j]
denote the maximum amount of money you can have on day i
if you have exactly j
shares of stock on that day. The final answer will be dp[N][0]
. This easily leads to an O(N^2)
DP.
Of course, we never used the fact that the DP is concave down! Specifically, let dif[i][j]=dp[i][j]-dp[i][j+1]\ge 0
. Then dif[i][j]\le dif[i][j+1]
for all j\ge 0
(ignoring the case when we get dp
values of -\infty
).
We'll process the shares in order. Suppose that on the current day shares are worth p
. We can replace (buy or sell a share) in the statement with (buy, then sell between 0 and 2 shares).
- If we currently have
j
shares and overall balanceb
, then after buying,j
increases by one andb
decreases byp
. The differences between every two consecutive elements do not change. - If we choose to buy a share, this is equivalent to setting
dp[i][j]=\max(dp[i][j],dp[i][j+1]+p)
for allj
. By the concavity condition,dp[i][j]=dp[i][j+1]+p
will hold for allj
less than a certain threshold whiledp[i][j+1]
will hold for all others. So this is equivalent to insertingp
into the list of differences while maintaining the condition that the differences are in sorted order. - So we add
p
to the list of differences two times. After that, we should pop the smallest difference in the list because we can't end up with a negative amount of shares.
(insert diagram)
(insert example)
The implementation is quite simple; maintain a priority queue that allows you to pop the minimum element.
My Solution
#include <bits/stdc++.h>
using namespace std;
int main() {
int N; cin >> N;
priority_queue<int,vector<int>,greater<int>> pq;
long long ans = 0;
for (int i = 0; i < N; ++i) {
int p; cin >> p; ans -= p;
pq.push(p); pq.push(p); pq.pop();
}
for (int i = 0; i < N; ++i) {
ans += pq.top();
pq.pop();
}
cout << ans << "\n";
}
Potatoes
Let dif_i=a_i-b_i
. Defining d_j=\sum_{i=1}^jdif_i
, our goal is to move around the potatoes such that d_0,d_1,\ldots,d_N
is a non-decreasing sequence. Moving a potato is equivalent to changing exactly one of the d_i
(aside from d_0,d_N
) by one.
Slow Solution: Let dp[i][j]
be the minimum cost to determine d_0,d_1,\ldots,d_i
such that d_i\le j
for each 0\le j\le d_N
. This gives a O(N\cdot d_N)
solution.
As before, this DP is concave up for a fixed i
! Given a piecewise linear function DP_x
, we need to support the following operations.
- Add
|x-k|
to the function for somek
- Set
DP_x=\min(DP_x,DP_{x-1})
for allx
Again, these can be done with a priority queue in O(N\log N)
time!
My Solution
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
ll fst = 0; // value of DP function at 0
priority_queue<ll> points; // points where DP function changes slope
int main() {
cin >> N;
vector<ll> dif(N+1);
for (int i = 1; i <= N; ++i) {
int a,b; cin >> a >> b;
dif[i] = a-b+dif[i-1];
}
assert(dif[N] >= 0);
for (int i = 1; i < N; ++i) {
if (dif[i] < 0) fst -= dif[i], dif[i] = 0;
fst += dif[i];
points.push(dif[i]); points.push(dif[i]);
points.pop();
}
while (points.size()) {
ll a = points.top(); points.pop();
fst -= min(a,dif[N]);
}
cout << fst << "\n";
}
Landscaping
This is quite similar to the previous task, so it's easy to guess that slope trick is applicable.
Again, let's first come up with a slow DP. Let dp[i][j]
equal the number of ways to move dirt around the first i
flowerbeds such that the first i-1
flowerbeds all have the correct amount of dirt while the $i$-th flowerbed has j
extra units of dirt (or lacks -j
units of dirt if j
is negative). The answer will be dp[N][0]
.
This DP is concave up for any fixed i
. To get dp[i+1]
from dp[i]
we must be able to support the following operations.
- Shift the DP curve
A_i
units to the right. - Shift the DP curve
B_i
units to the left. - Add
Z\cdot |j|
toDP[j]
for allj
. - Set
DP[j] = \min(DP[j],DP[j-1]+X)
andDP[j] = \min(DP[j],DP[j+1]+Y)
for allj
.
As before, it helps to look at the differences dif[j]=DP[j+1]-dif[j]
instead. Then the last operation is equivalent to the following:
- For all
j\ge 0
, we setdif[j] = \min(dif[j]+Z,X)
- For all
j<0
, we setdif[j] = \max(dif[j]-Z,-Y)
.
If we maintain separate deques for dif
depending on whether j\ge 0
or j<0
and update all of the differences in the deques "lazily" then we can do this in O(\sum A_i+\sum B_i)
time.
Bonus: Solve this problem when \sum A_i+\sum B_i
is not so small.
My Solution
#include <bits/stdc++.h>
using namespace std;
int N,X,Y,Z;
int difl, difr;
deque<int> L, R;
long long ans;
void rig() { // shift right A
if (L.size() == 0) L.push_back(-Y-difl);
int t = L.back()+difl; L.pop_back();
t = max(t,-Y); ans -= t;
R.push_front(t-difr);
}
void lef() { // shift left B
if (R.size() == 0) R.push_front(X-difr);
int t = R.front()+difr; R.pop_front();
t = min(t,X); ans += t;
L.push_back(t-difl);
}
int main() {
freopen("landscape.in","r",stdin);
freopen("landscape.out","w",stdout);
cin >> N >> X >> Y >> Z;
for (int i = 0; i < N; ++i) {
int A,B; cin >> A >> B;
for (int j = 0; j < A; ++j) rig(); // or we can just do |A-B| shifts in one direction
for (int j = 0; j < B; ++j) lef();
difl -= Z, difr += Z; // adjust slopes differently for left and right of j=0
}
cout << ans << "\n";
}
Problems
- Wall
- same as "Potatoes"
- Stock Trading (Camp)
- extension of "Buy Low Sell High"
- Amount of shares can go negative, but you can never have more than
L
shares or less than-L
.
- Bookface
- CCDSAP Exam
- basically same as Bookface
- Farm of Monsters
- Moving Walkways
- April Fools' Problem
- binary search on top of slope trick
- Conquer the World
- ICPC world finals, 0 solves in contest
- "Potatoes" on tree!!