Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 9301 Accepted Submission(s): 2893
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
题意概括:有N个数字需要输出,输出过程中可以随意换行。每行需要付出常数M的额外代价。若一行中连续输出(只输出一个也算),则需要的代价依照上述公式计算。
求代价最小值。
很容易想到DP解决。
设f[i]表示输出前i个数字的花费,则有转移方程$f[i]=min(f[i],f[j]+(sum[i]-sum[j])^2+M),0<j<i$
n范围很大,按这个式子暴力动规会TLE,所以需要斜率优化。
若有k满足0<k<j<i ,且用k算比用j优(算出的花费更小),则有 $f[k]+(sum[i]-sum[k])^2+M<=f[j]+(sum[i]-sum[j])^2+M))$
各种变形化简得到:$[(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i]$
设:$ y=dp[j]+sum[j]*sum[j] $ $x=2*sum[j]$
可以得到斜率表达式:$(yj-yk)/(xj-xk) <= sum[i] $
据此维护一个斜率逐渐增大的队列来dp,即可快速出解
1 /**/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<algorithm> 7 using namespace std; 8 const int mxn=500020; 9 int f[mxn]; 10 int sum[mxn]; 11 int q[mxn]; 12 int n,m; 13 int up(int x,int y){//式子的分子 14 return (f[x]+sum[x]*sum[x])-(f[y]+sum[y]*sum[y]); 15 } 16 int down(int x,int y){//式子的分母 17 return 2*(sum[x]-sum[y]); 18 } 19 int dp(int x,int y){//dp值 20 return f[y]+(sum[x]-sum[y])*(sum[x]-sum[y])+m; 21 } 22 int main(){ 23 while(scanf("%d%d",&n,&m)!=EOF){ 24 int i,j; 25 int hd=0,tl=0; 26 sum[0]=0;f[0]=0; 27 for(int x,i=1;i<=n;i++){ 28 scanf("%d",&x); 29 sum[i]=sum[i-1]+x; 30 } 31 q[tl++]=0; 32 for(i=1;i<=n;i++){ 33 while(hd+1<tl && up(q[hd+1],q[hd])<=sum[i]*down(q[hd+1],q[hd]))hd++; 34 f[i]=dp(i,q[hd]); 35 while(hd+1<tl && up(i,q[tl-1])*down(q[tl-1],q[tl-2])<=up(q[tl-1],q[tl-2])*down(i,q[tl-1]))tl--; 36 q[tl++]=i; 37 } 38 printf("%d\n",f[n]); 39 } 40 return 0; 41 }