令 (f_i) 表示前 (i) 个玩具都已经放入容器的最小费用。对 (C) 做一遍前缀和。
转移直接枚举上一次分段:
[f_i=min{f_j+(i-j-1+C_i-C_j-L)^2|j<i}
]
考虑两个决策 (j,k(j<k)),若 (j) 比 (k) 优:
[f_j+(i-j+C_i-C_j-L-1)^2<f_k+(i-k+C_i-C_k-L-1)^2
]
[f_j-f_k<(C_i+i-L-1-C_k-k)^2-(C_i+i-L-1-C_j-j)^2
]
设 (t=C_i+i-L-1)
[f_j-f_k<(t-C_k-k)^2-(t-C_j-j)^2
]
[f_j-f_k<t^2-2 imes t imes(C_k+k)+(C_k+k)^2-t^2+2 imes t imes(C_j+j)-(C_j+j)^2
]
[f_j+(C_j+j)^2-f_k-(C_k+k)^2<2 imes t imes(C_j+j-C_k-k)
]
[dfrac{f_j+(j+C_j)^2-f_k-(k+C_k)^2}{C_j+j-C_k-k}>2 imes t
]
因为 (t) 随着 (i) 的增大单调不减,所以对 ((C_i+i,f_i+(i+C_i)^2)) 维护一个上凸包。
时间复杂度 (Oleft(n ight))。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 50005
#define Db double
#define For(i,x,y)for(i=x;i<=(y);i++)
int que[N];
ll f[N],c[N];
inline ll sqr(ll x)
{
return x*x;
}
inline Db slope(int j,int k)
{
return Db(f[j]+sqr(j+c[j])-f[k]-sqr(k+c[k]))/Db(c[j]+j-c[k]-k);
}
int main()
{
int n,l,i,head,tail;
head=tail=1;
scanf("%d%d",&n,&l);
For(i,1,n)scanf("%d",&c[i]),c[i]+=c[i-1];
For(i,1,n)
{
while(head<tail&&slope(que[head],que[head+1])<=(c[i]+i-l-1)<<1)head++;
/*printf("%d
",que[head]);*/
f[i]=f[que[head]]+sqr(i-que[head]-1+c[i]-c[que[head]]-l);
while(head<tail&&slope(que[tail-1],que[tail])>=slope(que[tail-1],i))tail--;
que[++tail]=i;
}
printf("%lld",f[n]);
return 0;
}