玩具装箱
luogu 3195
题目大意
有n件物品,每件物品有相对的长度CiC_iCi现在要把这n件物品放到容器中,切放的物品必须是连续的,若把第i件物品到第j件物品放到一个容器中,那此容器的长度定义为x=j−i+∑k=ijCix=j−i+\sum_{k=i}^{j} C_ix=j−i+∑k=ijCi,此容器的费用即为(x−L)2(x-L)^2(x−L)2(L是常数),现在问你把所有物品放倒容器中,费用之和最小是多少
输入样例
5 4
3
4
2
1
4
输出样例
1
数据范围
对于全部的测试点,1≤n≤5×1041 \leq n \leq 5 \times 10^41≤n≤5×104,1≤L≤1071 \leq L \leq 10^71≤L≤107,1≤Ci≤1071 \leq C_i \leq 10^71≤Ci≤107。
解题思路
我们设f[i]为把1至i的所有物品放进容器中的最小费用,那我们可以得出转移方程:
fi=fj+(i−(j+1)+sumi−sumj−L)2f_i=f_j+(i-(j+1)+sum_i-sum_j-L)^2fi=fj+(i−(j+1)+sumi−sumj−L)2
注:sum是C的前缀和,这个范围是j+1到i所以减的是j+1,前缀和减的是sum[(j+1)-1]
但是会超时间,所以我们要考虑优化
我们可以变式为:
fi=fj+((i+sumi−1−L)−(j+sumj))2f_i=f_j+((i+sum_i-1-L)-(j+sum_j))^2fi=fj+((i+sumi−1−L)−(j+sumj))2
然后把平方拆开,得到:
fi=fj+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(j+sumj)+(j+sumj)2f_i=f_j+(i+sum_i-1-L)^2-2*(i+sum_i-1-L)*(j+sum_j)+(j+sum_j)^2fi=fj+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(j+sumj)+(j+sumj)2
若有决策点a,b满足a>b且a比b优
则
fa+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(a+suma)+(a+suma)2<fj+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(b+sumb)+(b+sumb)2f_a+(i+sum_i-1-L)^2-2*(i+sum_i-1-L)*(a+sum_a)+(a+sum_a)^2<f_j+(i+sum_i-1-L)^2-2*(i+sum_i-1-L)*(b+sum_b)+(b+sum_b)^2fa+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(a+suma)+(a+suma)2<fj+(i+sumi−1−L)2−2∗(i+sumi−1−L)∗(b+sumb)+(b+sumb)2
变式
fa−2∗(i+sumi−1−L)∗(a+suma)+(a+suma)2<fj−2∗(i+sumi−1−L)∗(b+sumb)+(b+sumb)2f_a-2*(i+sum_i-1-L)*(a+sum_a)+(a+sum_a)^2<f_j-2*(i+sum_i-1-L)*(b+sum_b)+(b+sum_b)^2fa−2∗(i+sumi−1−L)∗(a+suma)+(a+suma)2<fj−2∗(i+sumi−1−L)∗(b+sumb)+(b+sumb)2
(fa+(a+suma)2)−(fb+(b+sumb)2)<2∗(i+sumi−1−L)∗((a+suma)−(b+sumb))(f_a+(a+sum_a)^2) - (f_b+(b+sum_b)^2)<2*(i+sum_i-1-L)*((a+sum_a)-(b+sum_b))(fa+(a+suma)2)−(fb+(b+sumb)2)<2∗(i+sumi−1−L)∗((a+suma)−(b+sumb))
((fa+(a+suma)2)−(fb+(b+sumb)2))/((a+suma)−(b+sumb))<2∗(i+sumi−1−L)((f_a+(a+sum_a)^2) - (f_b+(b+sum_b)^2))/((a+sum_a)-(b+sum_b))<2*(i+sum_i-1-L)((fa+(a+suma)2)−(fb+(b+sumb)2))/((a+suma)−(b+sumb))<2∗(i+sumi−1−L)
设
xi=i+sumix_i=i+sum_ixi=i+sumi
yi=fi+(i+sumi)2y_i=f_i+(i+sum_i)^2yi=fi+(i+sumi)2
则
(ya−yb)/(xa−xb)<2∗(i+sumi−1−L)(y_a - y_b)/(x_a-x_b)< 2*(i+sum_i-1-L)(ya−yb)/(xa−xb)<2∗(i+sumi−1−L)
带入模板即可
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, L, l, r, a[50010], sum[50010], x[50010], y[50010], q[50010], f[50010];
int main()
{scanf("%lld %lld", &n, &L);for (ll i = 1; i <= n; ++i){scanf("%lld", &a[i]);sum[i] = sum[i - 1] + a[i];x[i] = sum[i] + i;}memset(f, 127/3, sizeof(f));f[0] = 0;q[1] = 0;l = 1;r = 1;for (ll i = 1; i <= n; ++i){while(l < r && (y[q[l + 1]] - y[q[l]]) <= 2 * (x[i] - 1 - L) * (x[q[l + 1]] - x[q[l]]))l++;//模板f[i] = f[q[l]] + (x[i] - x[q[l]] - 1 - L) * (x[i] - x[q[l]] - 1 - L);y[i] = f[i] + x[i] * x[i];while(l < r && (y[q[r]] - y[q[r - 1]]) * (x[i] - x[q[r]]) >= (y[i] - y[q[r]]) * (x[q[r]] - x[q[r - 1]])) r--;q[++r] = i;}printf("%lld", f[n]);return 0;
}