【斜率优化】玩具装箱(luogu 3195)

玩具装箱

luogu 3195

题目大意

有n件物品,每件物品有相对的长度 C i C_i Ci现在要把这n件物品放到容器中,切放的物品必须是连续的,若把第i件物品到第j件物品放到一个容器中,那此容器的长度定义为 x = j − i + ∑ k = i j C i x=j−i+\sum_{k=i}^{j} C_i x=ji+k=ijCi,此容器的费用即为 ( x − L ) 2 (x-L)^2 (xL)2(L是常数),现在问你把所有物品放倒容器中,费用之和最小是多少

输入样例

5 4
3
4
2
1
4

输出样例

1

数据范围

对于全部的测试点, 1 ≤ n ≤ 5 × 1 0 4 1 \leq n \leq 5 \times 10^4 1n5×104 1 ≤ L ≤ 1 0 7 1 \leq L \leq 10^7 1L107 1 ≤ C i ≤ 1 0 7 1 \leq C_i \leq 10^7 1Ci107

解题思路

我们设f[i]为把1至i的所有物品放进容器中的最小费用,那我们可以得出转移方程:
f i = f j + ( i − ( j + 1 ) + s u m i − s u m j − L ) 2 f_i=f_j+(i-(j+1)+sum_i-sum_j-L)^2 fi=fj+(i(j+1)+sumisumjL)2
注:sum是C的前缀和,这个范围是j+1到i所以减的是j+1,前缀和减的是sum[(j+1)-1]
但是会超时间,所以我们要考虑优化
我们可以变式为:
f i = f j + ( ( i + s u m i − 1 − L ) − ( j + s u m j ) ) 2 f_i=f_j+((i+sum_i-1-L)-(j+sum_j))^2 fi=fj+((i+sumi1L)(j+sumj))2
然后把平方拆开,得到:
f i = f j + ( i + s u m i − 1 − L ) 2 − 2 ∗ ( i + s u m i − 1 − L ) ∗ ( j + s u m j ) + ( j + s u m j ) 2 f_i=f_j+(i+sum_i-1-L)^2-2*(i+sum_i-1-L)*(j+sum_j)+(j+sum_j)^2 fi=fj+(i+sumi1L)22(i+sumi1L)(j+sumj)+(j+sumj)2
若有决策点a,b满足a>b且a比b优

f a + ( i + s u m i − 1 − L ) 2 − 2 ∗ ( i + s u m i − 1 − L ) ∗ ( a + s u m a ) + ( a + s u m a ) 2 < f j + ( i + s u m i − 1 − L ) 2 − 2 ∗ ( i + s u m i − 1 − L ) ∗ ( b + s u m b ) + ( b + s u m b ) 2 f_a+(i+sum_i-1-L)^2-2*(i+sum_i-1-L)*(a+sum_a)+(a+sum_a)^2fa+(i+sumi1L)22(i+sumi1L)(a+suma)+(a+suma)2<fj+(i+sumi1L)22(i+sumi1L)(b+sumb)+(b+sumb)2
变式
f a − 2 ∗ ( i + s u m i − 1 − L ) ∗ ( a + s u m a ) + ( a + s u m a ) 2 < f j − 2 ∗ ( i + s u m i − 1 − L ) ∗ ( b + s u m b ) + ( b + s u m b ) 2 f_a-2*(i+sum_i-1-L)*(a+sum_a)+(a+sum_a)^2fa2(i+sumi1L)(a+suma)+(a+suma)2<fj2(i+sumi1L)(b+sumb)+(b+sumb)2
( f a + ( a + s u m a ) 2 ) − ( f b + ( b + s u m b ) 2 ) < 2 ∗ ( i + s u m i − 1 − L ) ∗ ( ( a + s u m a ) − ( b + s u m b ) ) (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+sumi1L)((a+suma)(b+sumb))
( ( f a + ( a + s u m a ) 2 ) − ( f b + ( b + s u m b ) 2 ) ) / ( ( a + s u m a ) − ( b + s u m b ) ) < 2 ∗ ( i + s u m i − 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+sumi1L)

x i = i + s u m i x_i=i+sum_i xi=i+sumi
y i = f i + ( i + s u m i ) 2 y_i=f_i+(i+sum_i)^2 yi=fi+(i+sumi)2

( y a − y b ) / ( x a − x b ) < 2 ∗ ( i + s u m i − 1 − L ) (y_a - y_b)/(x_a-x_b)< 2*(i+sum_i-1-L) (yayb)/(xaxb)<2(i+sumi1L)
带入模板即可

代码

#include
#include
#include
#include
#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;
}

你可能感兴趣的:(#,斜率优化)