题目大意:
给出N个单词,每个单词有个非负权值Ci,现要将它们分成连续的若干段,每段的代价为此段单词的权值和,还要加一个常数M,即(∑Ci)^2+M。现在想求出一种最优方案,使得总费用之和最小。
容易写出方程:
f[i]=min{f[j]+(s[i]-s[j])^2+M}(0<=j<=i-1)
f[i]=min{f[j]+(s[i]-s[j])^2+M}
展开得
f[i]=min{f[j]+s[i]^2+s[j]^2-2*s[i]*s[j]+M}
令f[i]=B,f[j]+s[j]^2=y,2*s[j]=x,k=s[i]
因此k*x+B=y
最小值就是下凸包(斜率递增)
最大值就是上凸包(斜率递减)
注意,队列里存的是点,两点之间的斜率保持单调
#include<bits/stdc++.h>
#define N 500005
using namespace std;
int n,m,a[N],s[N],f[N],q[N];
double K(int i,int j){return double((f[j]+s[j]*s[j])-(f[i]+s[i]*s[i]))/(2*(s[j]-s[i]));}
int main(){
freopen("1.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
int h=1,t=1;
for(int i=1;i<=n;i++){
while(h<t&&K(q[h],q[h+1])<=s[i]) h++;//将没用的答案弹出
f[i]=f[q[h]]+(s[i]-s[q[h]])*(s[i]-s[q[h]])+m;//队首为最优
while(h<t&&K(q[t-1],q[t])>=K(q[t],i)) t--;//将不满足单调性的点弹出直到满足单调性
q[++t]=i;//插入新的点
}cout<<f[n];return 0;
}