设状态f(i)表示将前i个玩具装箱后的最小费用,sum(i)表示前i个玩具的长度(c值)和
易得状态转移方程
其中 0 < j < i
为了简化式子,记
将上式代入,开方1
则对于所有可能的
记2
将上式移项后代入,得到
现在要使b最小,k不变,若固定x,则y越小,b越小,所以维护一个下凸壳。
可以看出x与查询斜率k均单调递增,维护一个有关斜率的单调队列即可。
#include <cstdio>
#define N 50005
using namespace std;
typedef long long LL;
const double INF=1e10;
inline LL sq(LL x) { return x*x; }
struct Point {
LL x,y;
Point() {}
Point(LL _x,LL _y):x(_x),y(_y) {}
double operator ^ (const Point& rhs) const { //get_slope
if(x==rhs.x) return y>rhs.y ? -INF : INF;
return (double)(rhs.y-y)/(rhs.x-x);
}
}q[N];
int n,l=1,r;
LL L,f[N],sum[N];
LL get_ans(LL k) {
while(l<r && (q[l]^q[l+1])<k) l++;
return q[l].y-k*q[l].x;
}
void Insert(Point o) {
while(l<r && (q[r-1]^q[r])>(q[r-1]^o)) r--;
q[++r]=o;
return ;
}
int main() {
scanf("%d%lld",&n,&L); L++;
for(int i=1;i<=n;i++) scanf("%lld",sum+i), sum[i]+=sum[i-1]+1;
//DP
q[++r]=Point(0,0);
for(int i=1;i<=n;i++) {
f[i]=get_ans(2*(sum[i]-L))+sq(sum[i]-L);
Insert(Point(sum[i],sum[i]*sum[i]+f[i]));
}
printf("%lld\n",f[n]);
return 0;
}