[斜率优化]洛谷 P3195 玩具装箱 题解
斜率优化板子
以下的 \(sum\) 代表前缀和,不难发现我们其实要求的就是下面这个东西
\[dp_i = \min(dp_j + (sum_i - sum_j + i - j - 1 - L) ^ 2)
\]
这里其实有一个技巧的。作者第一次做这个题的时候是给括号全打开了,化出来了一大堆常数。但是实际上完全算不对,而且没必要。
我们可以单独整两个函数
\[a(x) = sum_x + x
\]
\[b(x) = sum_x + x + L + 1
\]
这样就简单啦!原式等于(\(\min\) 函数省略)
\[dp_i = dp_j + (a(i) - b(j)) ^ 2
\]
\[dp_i = dp_j + a(i) ^ 2 - 2 a(i) b(j) + b(j) ^ 2
\]
然后搞一个斜率优化就行
\[dp_j + b(j) ^ 2 = 2 a(i)b(j) - a(i) ^ 2 + dp_i
\]
符合 \(y = kx + b\) 的形式
其中 \(y = dp_j + b(j) ^ 2\)
\(k = 2a(i)\)
\(x = b(j)\)
\(b = -a(i)^2 + dp_i\)
然后这题就完了
#include <bits/stdc++.h>
#define qwq return 0
#define N 500007
#define int long long
int h , t , n , L , c[N] , dp[N] , q[N];
namespace P3195 {
using namespace std;
inline int a(int x) {
return c[x] + x;
}
inline int b(int x) {
return a(x) + L + 1;
}
inline double slope(int i , int j) {
return (dp[i] + b(i) * b(i) - dp[j] - b(j) * b(j)) * 1.0 / (b(i) - b(j));
}
void Kx() {
ios:: sync_with_stdio(0) , cin.tie(NULL) , cout.tie(NULL);
cin >> n >> L;
for(register int i = 1; i <= n; ++i) {
cin >> c[i]; c[i] += c[i - 1];
}
h = 1 , t = 0;
for(register int i = 1; i <= n; ++i) {
while(h < t && slope(i - 1 , q[t]) <= slope(q[t - 1] , q[t])) {
--t;
}
q[++t] = i - 1;
while(h < t && slope(q[h] , q[h + 1]) <= 2 * a(i)) {
++h;
}
int target = q[h];
dp[i] = dp[target] + (a(i) - b(target)) * (a(i) - b(target));
}
cout << dp[n];
}
}
signed main() {
P3195 :: Kx();
qwq;
}