[BZOJ1010/Luogu3195][HNOI2008]玩具装箱TOY
题目链接:
首先很容易地我们可以得到一个\(O(n^2)\)的算法:
设\(f_{[i]}\)表示前\(i\)个玩具的最小费用,\(Sum_{[i]}\)表示前\(i\)个玩具的长度和,则有转移方程:
\[f_{[i]}=\min_{0\le j<i}\{f_{[j]}+(Sum_{[i]}-Sum_{[j]}+i-j-1-L)^2\}
\]
设\(A_{[x]}=Sum_{[x]}+x\),\(B_{[x]}=Sum_{[x]}+x+1+L\),则:
\[f_{[i]}=\min_{0\le j<i}\{f_{[j]}+(A_{[i]}-B_{[j]})^2\}
\]
\[f_{[i]}=f_{[j]}+A_{[i]}^2-2A_{[i]}B_{[j]}+B_{[j]}^2
\]
\[2A_{[i]}B_{[j]}+f_{[i]}-A_{[i]}^2=f_{[j]}+B_{[j]}^2
\]
设\(X_{[x]}=B_{[x]}\),\(Y_{[x]}=f_{[j]}+B_{[j]}^2\),则:
\[2A_{[i]}X_{[j]}+f_{[i]}-A_{[i]}^2=Y_{[j]}
\]
若使\(f_{[i]}\)最小,那么将上式看做一条斜率为\(2A_{[i]}\)的直线,使\(f_{[i]}-A_{[i]}^2\)最小,则有一点\((X_{[j]},Y_{[j]})\)到直线距离最短。
显然,此点一定在所有决策点组成的下凸包上。
那么套斜率优化维护下凸包就好了。
时间复杂度 \(O(n)\)(均摊)
#pragma GCC optimize(3)
#include <cstdio>
#include <cctype>
char File[300005],*p1=File,*p2=File;
inline char Getchar()
{
return p1==p2&&(p2=(p1=File)+fread(File,1,300000,stdin),p1==p2)?EOF:*p1++;
}
inline int Getint()
{
register int x=0;
register char c;
while(!isdigit(c=Getchar()));
for(;isdigit(c);c=Getchar())x=x*10+c-48;
return x;
}
int n,l,q[50005],qh,qt;
long long c[50005],f[50005];
inline long long A(int x){return c[x]+x;}
inline long long B(int x){return c[x]+x+l+1;}
inline long long X(int x){return B(x);}
inline long long Y(int x){return f[x]+B(x)*B(x);}
int main()
{
n=Getint(),l=Getint();
for(register int i=1;i<=n;++i)c[i]=Getint()+c[i-1];
for(register int i=1;i<=n;++i)
{
while(qh<qt&&Y(q[qh+1])-Y(q[qh])<=2*A(i)*(X(q[qh+1])-X(q[qh])))++qh;
f[i]=f[q[qh]]+(A(i)-B(q[qh]))*(A(i)-B(q[qh]));
while(qh<qt&&(Y(q[qt])-Y(q[qt-1]))*(X(i)-X(q[qt]))>=(Y(i)-Y(q[qt]))*(X(q[qt])-X(q[qt-1])))--qt;
q[++qt]=i;
}
printf("%lld\n",f[n]);
return 0;
}