题意:
给出n,m,求在n个数中分成任意段;
每段的花销是(sigma(a[l],a[r])+m)^2;
求这个最小值;
题解:
斜率优化入门题;
然而入门并不是这么容易的(笑);
我们可以很容易的得到dp方程;
f[i]=f[j]+(s[i]-s[j]+m)^2
其中s[x]表示x的前缀和,0<=j<i;
O(n^2)显然超时;
对于dp方程我们把平方展开可得;
f[i] = f[j]+(s[i]+m)^2+s[j]^2-2(s[i]+m)s[j];
于是对于两个决策j,k来说 (设k<j)
倘若j优于k,那么f[j]+(s[i]+m)^2+s[j]^2-2(s[i]+m)s[j] < f[k]+(s[i]+m)^2+s[k]^2-2(s[i]+m)s[k];
化简 f[j]+s[j]^2-f[k]-s[k]^2 < 2(s[i]+m)(s[j]-s[k])
(f[j]+s[j]^2-f[k]-s[k]^2)/(s[j]-s[k]) < 2(s[i]+m)
发现无论对于任意的i来说,左面都是常量;
而右面则是关于i递增的;
那么我们就可以确定,当j,k满足以上条件时,再对于后面的i来说,j也总优于k,所有可以将k删掉;
而反之则不行;
为了方便表述,设f[x]+s[x]^2=g[x];
那么上式就为(g[j]-g[k])/(s[j]-s[k])
与斜率的表述形式相似吧;
我们需要在O(1)的时间复杂度内找到最优的决策,所以我们可以考虑维护队列优化dp;
由上面的结论我们可以一一把队首干掉而使队首最优;
但这种想法有反例存在,就是在三个决策,k<j<i;
当i与k都比j优时,我们无法得知i与k孰优孰劣;
所以我们需要对此作出判断,讨论jk斜率大于ij斜率的时候;
如果2s[i]大于两个斜率,那么应为i最优,jk可以删掉;
如果2s[i]大于ij斜率而小于jk斜率,那么就是i优于j而且k优于j;
因为i已经比j优,所以j可以删掉;
如果2s[i]小于两个斜率,那么当前最优为k,并且我们无法删掉点;
但是如果讨论后面s[i]继续增大的情况,我们发现与前两个相同;
j就不可能为最优值,所以我们在这里依然可以删掉j;
那么jk斜率大于ij斜率的情况不可能出现了,因为那时j会被删掉;
因此斜率是单调递增的,即图像为下凹曲线;(所以某题解说的是错的啦(笑))
实现的时候就是对于每个需要更新的i;
I:取队首,判断其是否比第二项优,不优就删掉,直到优为止;
II:这时队首就是最优的决策,用其更新i;
III:将i加入队列之前,我们要维护斜率单调性;
IV:把i加进去;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 500005
using namespace std;
typedef long long ll;
ll a[N],s[N],f[N],q[N];
int main()
{
int n,i,j,k,st,en;
ll m;
while(scanf("%d%I64d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++)
{
scanf("%I64d",a+i);
s[i]=s[i-1]+a[i];
}
q[st=en=1]=0;
for(i=1;i<=n;i++)
{
while(st<en&&(f[q[st]]-f[q[st+1]]+s[q[st]]*s[q[st]]-s[q[st+1]]*s[q[st+1]])>=2*s[i]*(s[q[st]]-s[q[st+1]]))
{
st++;
}
f[i]=f[q[st]]+(s[i]-s[q[st]])*(s[i]-s[q[st]])+m;
while(st<en&&(f[q[en]]-f[q[en-1]]+s[q[en]]*s[q[en]]-s[q[en-1]]*s[q[en-1]])*(s[i]-s[q[en]])>=(f[i]-f[q[en]]+s[i]*s[i]-s[q[en]]*s[q[en]])*(s[q[en]]-s[q[en-1]]))
{
en--;
}
q[++en]=i;
}
printf("%I64d\n",f[n]);
}
}