【BZOJ1010】【HNOI2008】玩具装箱toy 解题报告

wangyuchen

2018-03-17 12:37:50

Personal

## 思路: 这题不难想。 首先我们先推出一个普通的dp方程: $f_i = min \{ f_j+(i-j-1+sum_i-sum_j-L)^2\}$ 然后就推一推式子了: $f_j+(i-j-1+sum_i-sum_j-L)^2 < f_k+(i-k-1+sum_i-sum_k-L)^2$ 令$num_i = i+sum_i$ 令$C = L+1$ $f_j+(num_i-num_j-L-1)^2 < f_k+(num_i-num_j-L-1)^2$ $f_j+{num_i}^2-2*num_i*(num_j+C)+(num_j+C)^2$ $<f_k+{num_i}^2-2*num_i*(num_k+C)+(num_k+C)^2$ $f_j+(num_j+C)^2-f_k-(num_k+C)^2 < $ $2*num_i*(num_j-num_k)$ ${f_j+(num_j+C)^2-f_k-(num_k+C)^2 \over 2*(num_j-num_k)} < num_i$ 接下来就可以用斜率优化dp了。 ## 代码: ```cpp #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> using namespace std; const int N = 50010; inline long long sqr(long long x) { return x*x; } long long a[N]; long long sum[N]; long long num[N], C; long long f[N]; double calc(int j, int k) { return (double)(f[j]+sqr(num[j]+C)-f[k]-sqr(num[k]+C))/(double)(2*(num[j]-num[k])); } int Q[N], hd, tl; int main() { int n; long long m; scanf("%d%lld", &n, &m); for(int i=1; i<=n; i++) { scanf("%lld", &a[i]); sum[i] = sum[i-1]+a[i]; } for(int i=1; i<=n; i++) num[i] = sum[i]+i; C = m+1; Q[hd = 0] = 0; tl = 1; for(int i=1; i<=n; i++) { while(hd < tl-1 && calc(Q[hd+1], Q[hd]) <= num[i]) hd++; f[i] = f[Q[hd]] + sqr(num[i]-num[Q[hd]]-C); while(hd < tl-1 && calc(i, Q[tl-1]) <= calc(Q[tl-1], Q[tl-2])) tl--; Q[tl++] = i; } printf("%lld\n", f[n]); return 0; } ```