【题解】
设 f[i]表示将第1~i个物品装箱后,费用的最小值,答案为f[n]
s[i]=c[1]+c[2]+……+c[i]
则:f[0]=0
f[i]=min{ f[j]+(s[i]-s[j]+i-j-1-L)^2 } ( i>0 , 0<=j<i )
================================================================================
考虑斜率优化:
对于任意i>1,若j不比k差( 0<=j,k<i , j!=k )
则:f[j]+(s[i]-s[j]+i-j-1-L)^2 <= f[k]+(s[i]-s[k]+i-k-1-L)^2 设 x[i]=s[i]+i,如此,与j,k有关的变量各少一个,且j,k形式对应
=> f[j]+(x[i]-x[j]-L-1)^2 <= f[k]+(x[i]-x[k]-L-1)^2 设 mi=x[i]-L-1,如此,与j,k无关的量仅剩一个
=> f[j]+(mi-x[j])^2 <= f[k]+(mi-x[k])^2
=> f[j]+x[j]^2-2*mi*x[j] <= f[k]+x[k]^2-2*mi*x[k]
=> f[j]+x[j]^2-f[k]-x[k]^2 <= 2*mi*(x[j]-x[k]) 设 y[i]=f[i]+x[i]^2
=> y[j]-y[k] <= 2*mi*(x[j]-x[k])
∵ x单调递增,设 K[j,k]=(y[j]-y[k])/(x[j]-x[k]) 即斜率
得到结论:若k<j,K[j,k]<=2*mi (斜率小,后优)
若j<k,K[j,k]>2*mi (斜率大,前优)
================================================================================
构造下凸函数:
若 j<k<l,且j-k-l连线上凸(即K[j,k]>K[k,l])
则有:若 K[j,k]>2*m*i,则j比k优
若 K[j,k]<=2*m*i,则K[k,l]<K[j,k]<=2*m*i,l比k优
即 上凸、不凸时k对i永远不会最优,可删去k
∴ j-k-l下凸
将符合此条件的j,k,l,……依次加入队列,j,k为前两项
若 对于一个i,有K[j,k]<=2*mi (k比j优)
∵ m单调递增
∴ 对于t>i,必有 K[j,k]<=2*mt (k总比j优)
此时可删去队首j
================================================================================
注意边界:j>=0 -> 0要先纳入队列
【代码】
#include<stdio.h>
#include<stdlib.h>
typedef long long LL;
LL f[50005]={0},s[50005]={0},x[50005]={0},y[50005]={0};
int q[50005]={0};
double K(int i,int j)//其实会有浮点误差,改成long long乘法比较好
{
return (double)(y[i]-y[j])/(double)(x[i]-x[j]);
}
int main()
{
LL L,m;
int n,i,head=0,tail=0;
scanf("%d%lld",&n,&L);
for(i=1;i<=n;i++)
{
scanf("%lld",&s[i]);
s[i]+=s[i-1];
}
for(i=1;i<=n;i++)//0已加入
{
x[i]=s[i]+i;
m=x[i]-L-1;
while(head<tail&&K(q[head],q[head+1])<=(double)2*m) head++;//删去不是最优的队首元素
f[i]=f[q[head]]+(m-x[q[head]])*(m-x[q[head]]);//此时队首元素即为最优值,用它更新i
y[i]=f[i]+x[i]*x[i];
while(tail>head&&K(q[tail-1],q[tail])>=K(q[tail],i)) tail--;//维护下凸壳
q[++tail]=i;//将i入队
}
printf("%lld",f[n]);
return 0;
}