设f[i]表示前i件玩具装好所花的最小花费。
设sumc[i]表示前i件玩具的c之和。假如第k+1~i件玩具放在一起,那么间隔有i-k-1,所需的c为sumc[i]-sumc[k],所以花费为(sumc[i]+i-sumc[k]-k-1-L)^2。
所以有状态转移方程:。
现在来对这个方程简化:
设a[i]=sumc[i]+i,b[i]=sumc[i]+i+L+1。那么方程可化简为:。
展开,去掉min,移项可得:。
由于f[i]要取到最小值,且a[i]满足递增,所以此处只需用单调队列维护一个下凸包即可。
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=50005;
int N,q[MAXN];
LL L,sumc[MAXN],f[MAXN],a[MAXN],b[MAXN];
char c;
void scan(int &x)
{
for(c=getchar();c<'0'||c>'9';c=getchar());
for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}
void scan(LL &x)
{
for(c=getchar();c<'0'||c>'9';c=getchar());
for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}
int main()
{
int i;
LL x;
scan(N);scan(L);
for(i=1;i<=N;i++)
{
scan(x);
sumc[i]=sumc[i-1]+x;
a[i]=sumc[i]+i;
b[i]=sumc[i]+i+L+1;
}
b[0]=L+1;
memset(f,0x3f,sizeof(f));
f[0]=0;
int L=1,R=1;
for(i=1;i<=N;i++)
{
while(L<R&& (f[q[L+1]]+b[q[L+1]]*b[q[L+1]]-f[q[L]]-b[q[L]]*b[q[L]]) <= 2ll*a[i]*(b[q[L+1]]-b[q[L]]))
L++;
f[i]=f[q[L]]+(a[i]-b[q[L]])*(a[i]-b[q[L]]);
while(L<R&&(f[i]+b[i]*b[i]-f[q[R]]-b[q[R]]*b[q[R]])*(b[q[R]]-b[q[R-1]])<=(f[q[R]]+b[q[R]]*b[q[R]]-f[q[R-1]]-b[q[R-1]]*b[q[R-1]])*(b[i]-b[q[R]]))
R--;
q[++R]=i;
}
cout<<f[N];
return 0;
}