题意:输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M,让我们求这个费用的最小值(n=5e5)
5 5
5 9 5 7 5
230
样例解释:5^2+5 9^2+5 5^2+5 7^2+5 5^2+5 sum=230
设dp[i]表示输出到i的时候最少的花费,S[i]表示从a[1]到a[i−1]的数字和。注意这里为了方便起见前缀和与一般的有区别。 则有:
dp[i]=min{dp[j]+(S[i+1]−S[j])^2+M}(j<i)
下面给出斜率DP的优化解释:
设
当且仅当,且上式成立时,
对更新
比
更新
优
当一个数的值求完了,它的
值也跟着确定,我们就可以在空间中绘制出点
。
这个点代表已经求出值的一个点。
设
现在从左到右,设,
,那么
点便永远不可能成为最优解。为什么呢?
我们假设,也就是
点比
点优,排除j点。
如果,那么此时
点比
点更优,但是同时
,
说明还有k点比j点优。
设
由于我们排除了最优的情况,所以整个有效点集呈现一种上凸性质,即
的斜率要大于
的斜率。
这样,从左到右,斜率之间就是单调递减的了。当我们的最优解取得在点的时候,那么
点不可能再取得比
点更优的解了,于是
点也可以排除。换句话说,
点之前的点全部不可能再比
点更优了,可以全部从解集中排除。
从队头开始,如果已有元素
,当
点要求解时,如果
,那么说明
点比
点更优,
点可以排除,于是
出队
那么当要入队的时候,我们维护队列的上凸性质,即如果
,那么就将
点删除。直到找到
为止,并将
点加入在该位置中。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
ll pre[500005],dp[500005];
int v[500005],q[500005];
ll f(int i){
return dp[i]+pre[i]*pre[i];
}
ll fenmu(int j,int k){
return (pre[j]-pre[k]);
}
ll fenzi(int j,int k){
return (f(j)-f(k));
}
int main(){
while(cin>>n>>m){
int head=0,tail=-1;
pre[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
pre[i]=pre[i-1]+v[i];
}
q[++tail]=0;
for(int i=1;i<=n;i++){
while(head<tail&&fenzi(q[head+1],q[head])<=2*pre[i]*fenmu(q[head+1],q[head])) ++head;
dp[i]=dp[q[head]]+(pre[i]-pre[q[head]])*(pre[i]-pre[q[head]])+m;
while(head<tail&&fenzi(i,q[tail])*fenmu(q[tail],q[tail-1])<=fenzi(q[tail],q[tail-1])*fenmu(i,q[tail])) --tail;
q[++tail]=i;
}
cout<<dp[n]<<endl;
}
}