洛谷題目傳送門html
一開始確定要把題目要求的式子給寫出來函數
咱們知道方差的公式\(s^2=\frac{\sum\limits_{i=1}^{m}(x_i-\overline x)^2}{m}\)優化
題目要乘\(m^2\)再輸出,因而spa
\(m^2s^2=m\sum\limits_{i=1}^{m}(x_i-\overline x)^2\)code
\(=m(\sum\limits_{i=1}^{m}x_i^2-2\overline{x}\sum\limits_{i=1}^{m}x_i+m\overline{x}^2)\)htm
\(=m\sum\limits_{i=1}^{m}x_i^2-(\sum\limits_{i=1}^{m}x_i)^2\)blog
因而只要最小化\(\sum\limits_{i=1}^{m}x_i^2\)便可。隊列
然而選\(m\)段很是很差辦。這時候能夠聯想到凸優化。設\(G_m\)表示選\(m\)段\(\sum\limits_{i=1}^{m}x_i^2\)的最小值,當\(m\)增大的時候\(G_m\)顯然會減少,憑蒟蒻的感性理解,多分出一段對答案的影響幅度也愈來愈小,也就是說\(G_x\)關於\(x\)的函數圖像大概是下凸的。get
咱們用一個斜率爲\(mid\)的直線去切這個凸包。顯然\(mid\)的下界取\([0,1]\)之間的斜率,是總路程平方級別的,上界是\(0\)。由於切線在凸包的下方,因此多選一段的代價不是\(+mid\)而是\(-mid\)。flash
update:蒟蒻棄用了用直線切凸包的理解方法,蒟蒻用導數思想理解DP凸優化的思路能夠看這裏
接下來就是斜率優化的過程。設\(f_i\)爲前\(i\)條路的最優答案,\(x_i\)爲路程長度的前綴和,寫出轉移方程
\(f_i=\min\limits_{j=0}^{i}\{f_j-2x_ix_j+x_j^2\}+x_i^2\)
決策\(j\)優於\(k\)當且僅當
\(f_j-2x_ix_j+x_j^2<f_k-2x_ix_k+x_k^2\)
\(\frac{f_j+x_j^2-f_k-x_k^2}{x_j-x_k}<2x_i\)
因而設\(y_i=f_i+x_i^2\),把決策當作點\((x_i,y_i)\),使用單調隊列就OK了。注意這裏要記\(c_i\)表示最優決策下將前\(i\)條路分出的段數。最後判斷\(c_n\)與\(m\)的關係來調整斜率。
因爲這一題的斜率確定不會有小數,故也沒必要擔憂二分中的一些邊界問題。
#include<cstdio> #define RG register #define R RG int #define G c=getchar() #define Calc(j,k) (y[j]-y[k])/(x[j]-x[k]) typedef long long LL; const int N=3009; int n,q[N],c[N]; double f[N],k[N],x[N],y[N]; inline int in(){ RG char G; while(c<'-')G; R x=c&15;G; while(c>'-')x=x*10+(c&15),G; return x; } inline double sqr(RG double x){ return x*x; } inline void work(R mid){//斜率優化 R h,t,i; for(h=t=i=1;i<=n;++i){ while(h<t&&k[h]<2*x[i])++h; f[i]=f[q[h]]+sqr(x[i]-x[q[h]])-mid;//每轉移一次要減一下mid y[i]=f[i]+sqr(x[i]); c[i]=c[q[h]]+1;//記錄段數 while(h<t&&k[t-1]>Calc(q[t],i))--t; k[t]=Calc(q[t],i);q[++t]=i; } } int main(){ n=in();R m=in(),l,r,mid,i; for(i=1;i<=n;++i)x[i]=x[i-1]+in(); l=-sqr(x[n]);r=0;//大體肯定下界 while(l<r){ work(mid=(l+r+1)/2);//注意負數的下取整問題 c[n]<=m?l=mid:r=mid-1; } work(l); printf("%.0lf\n",m*(f[n]+m*l)-sqr(x[n]));//先加回m*l return 0; }