P3195 [HNOI2008]玩具装箱
先推出转移方程
$$
f _ i = \min _ {j < i} \{ f _ j + (s _ i - s _ j + i - j - 1 - L) ^ 2 \}
$$
为了方便,令
$$
s _ i = s _ i +i
$$
$$
L = L + 1
$$
那么方程为
$$
f _ i = \min \{ f _ j + (s _ i - s _ j - L) ^ 2 \}
$$
化为斜率优化的形式
$$
f _ i - (s _i - L) ^ 2 = \min _ {j < i} \{ f _ j + s _ j ^ 2 + 2 s _ j (L - s _ i)\}
$$
因为 $s _ i$ 单增,$s _ i + i$ 也是单增,用单调队列维护这个凸包即可。
查看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <cstdio> using namespace std; typedef long long LL; const int N = 5e4 + 10, inf = 1e9; int hd = 1, tl, q[N]; int n, L, w[N]; LL s[N], f[N]; double slope(int a, int b) { return (double)(f[a] + s[a] * s[a] - f[b] - s[b] * s[b]) / (double)(s[a] - s[b]); } int main() { scanf("%d%d", &n, &L); for (int i = 1; i <= n; i++) scanf("%d", &w[i]); for (int i = 1; i <= n; i++) s[i] = s[i - 1] + w[i]; for (int i = 1; i <= n; i++) s[i] += i; L++; q[++tl] = 0; for (int i = 1; i <= n; i++) { while (hd < tl && slope(q[hd], q[hd + 1]) < 2 * (s[i] - L)) hd++; f[i] = f[q[hd]] + (s[i] - s[q[hd]] - L) * (s[i] - s[q[hd]] - L); while (hd < tl && slope(q[tl], q[tl - 1]) > slope(i, q[tl])) tl--; q[++tl] = i; } printf("%lld", f[n]); return 0; }
|