分析
预处理前缀和
这显然是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;
}