题意:输出序列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;
}
}