斜率优化DP.
原题链接
oiwiki题解
(均摘自oiwiki)
注意精度及k的正负性.
代码如下:
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define ld long double
inline ll read() {
ll x = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f = ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
inline void print(ll x) {
if (x < 0) putchar('-'), x = -x;
if (x < 10) putchar(x + '0');
else {
print(x / 10);
putchar(x % 10 + '0');
}
}
const int N = 5e4 + 10;
int n; ll L, c[N], pre[N], f[N]; ld s[N], x[N], y[N], k[N];
int q[N], l = 1, r = 0;
ld calc(int A, int B) {
return (y[B] - y[A]) / (x[B] - x[A]);
}
int main() {
n = read(); L = read(); ++L;
for (int i = 1; i <= n; ++i) {
c[i] = read(), pre[i] = pre[i - 1] + c[i], s[i] = i + pre[i];
x[i] = 1.0 * s[i]; k[i] = -2.0 * (L - s[i]);
}
q[++r] = 0;
for (int i = 1; i <= n; ++i) {
while (l < r && calc(q[l], q[l + 1]) <= k[i]) ++l;
f[i] = y[q[l]] - k[i] * x[q[l]] + 1.0 * (s[i] - L) * (s[i] - L); y[i] = f[i] + x[i] * x[i];
// printf("i = %d, f[i] = %lld\n", i, f[i]);
while (l < r && calc(q[r - 1], q[r]) >= calc(q[r], i)) --r;
q[++r] = i;
}
print((ll)f[n]); putchar('\n');
return 0;
}
总结能用斜率优化的转移方程:
- 本题:f[i] = min{f[j] + [(i + pre[i]) - (j - pre[j]) - (L + 1)]^2}, 1 <= j < n.
小trick:整体法. - P2900 [USACO08MAR]Land Acquisition G
f[i] = min{f[j - 1] + f[j - 1] + w[i]h[j]}, 1 <= j <= n. - P4072 [SDOI2016征途]
总结1:多项式val(i, j)包含i, j的乘积项时常适用斜率优化.