分析
预处理前缀和
这显然是dp。
设fi表示前i件玩具花的最小费用。
则有:
①边界条件:
②动态转移方程:fi=min(fj+(i−j−1+sumi−sumj−L)2);
③答案:fn。
直接求解,时间复杂度为O(n2),显然会TLE。
考虑斜率优化。
原来的方程太复杂了,我们先化简一下。
通过换元法化简动态转移方程,将参量、变量、常量分离。
记gi=i+sumi,gj=j+sumj,C=1+L,
∴fi=min(fj+(gi−gj−C)2)
再求解斜率方程。
为了加快求解,我们设法尽可能多的排除一些状态,这需要决策点之间的比较,我们要找出决策点比较的式子。
设当前要求fi,存在决策点k,j,k>j,满足决策k优于决策
∴fk+(gi−gk−C)2≤fj+(gi−gj−C)2
∴fk+(−gk−C)2+2gi(−gk−C)≤fj+(−gj−C)2+2gi(−gj−C)
∴fk+(gk+C)2−fj−(gj+C)2≤2gi(gk−gj)
∵k>j,sumk>sumj
∴k+sumk>j+sumj即gk>gj
∴slope(k,j)=fk+(gk+C)2−fj−(gj+C)2gk−gj≤2gi
不难发现,k和
近一步,∀i∈N+,决策点i可以表示为平面上的点
对于∀k,j∈N+,k>j,
①当slope(k,j)≤2gi时,决策点k优于决策点
②当slope(k,j)>2gi时,决策点k劣于决策点
可以将问题转化为平面上的点的问题。
现在问题转化为:
给定平面上n个在横纵坐标都单调递增的点,给定一个斜率
①插入点操作:
在第n个点
②询问点操作:
在点集X={(x1,y1),(x2,y2),...,(xn,yn)}中寻找一个点i,使得经过
③更改
将k变成
要寻找的点i,很明显
又由于k是不断增大的,所以凸壳上要找的点
我们只需要维护一个单调队列即可。
队首维护:
队首的点为A,
队尾维护:
队末三个点为A,
若
每个点进队一次,出队一次。
所以总的时间复杂度为
总结
总算彻底弄清斜率优化的严谨过程了。
代码
#include <cstdio>
#include <cctype>
typedef long long Lint;
const int N=65536;
int n,l;
int c[N];
Lint sum[N];
Lint g[N];
Lint C;
Lint f[N];
Lint x[N];
Lint y[N];
int q[N];
int qh,qt;
inline int Read(void)
{
int x=0; char c=getchar();
for (;!isdigit(c);c=getchar());
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
inline double Slope(int i,int j)
{
return ((double)y[i]-y[j])/(x[i]-x[j]);
}
int main(void)
{
n=Read(),l=Read();
for (int i=1;i<=n;i++) c[i]=Read();
for (int i=1;i<=n;i++) sum[i]=sum[i-1]+c[i];
C=1+l;
for (int i=1;i<=n;i++) g[i]=sum[i]+i;
f[0]=0;
x[0]=g[0];
y[0]=f[0]+(g[0]+C)*(g[0]+C);
q[qh=qt=1]=0;
for (int i=1;i<=n;i++)
{
for (;qh!=qt&&Slope(q[qh+1],q[qh])<=2.0*g[i];q[qh++]=0);
f[i]=f[q[qh]]+(g[i]-g[q[qh]]-C)*(g[i]-g[q[qh]]-C);
x[i]=g[i];
y[i]=f[i]+(g[i]+C)*(g[i]+C);
for (;qh<qt&&Slope(q[qt-1],i)<Slope(q[qt-1],q[qt]);q[qt--]=0);
q[++qt]=i;
}
printf("%lld\n",f[n]);
return 0;
}