BZOJ4518: [Sdoi2016]征途
n<=3000个数划分成m段,每段的权值为这一段数字的和,求段的最小方差乘上m平方。
所以就是求上边那组平方和的最小值,这个可以dp,f(i,j)表示分成i段,前j个数最小方差,
pre表示前缀和,这个式子可以用斜率优化或决策单调性解决。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 //#include<iostream> 6 using namespace std; 7 8 int n,m; 9 #define maxn 3011 10 int a[maxn],f[2][maxn],sum[maxn],cur; 11 int head,tail,que[maxn]; 12 double play(int x,int y) 13 { 14 return ((f[cur^1][x]-f[cur^1][y])*1.0/(sum[x]-sum[y])+sum[x]+sum[y])/2; 15 } 16 const int inf=0x3f3f3f3f; 17 int main() 18 { 19 scanf("%d%d",&n,&m); 20 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 21 sum[0]=0;for (int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; 22 cur=0;for (int i=1;i<=n;i++) f[cur][i]=inf;f[cur][0]=0; 23 for (int i=1;i<=m;i++) 24 { 25 cur^=1; 26 f[cur][0]=inf; 27 head=tail=0;que[tail++]=0; 28 for (int j=1;j<=n;j++) 29 { 30 while (tail-head>1 && play(que[head+1],que[head])<sum[j]) head++; 31 f[cur][j]=f[cur^1][que[head]]+(sum[j]-sum[que[head]])*(sum[j]-sum[que[head]]); 32 while (tail-head>1 && play(que[tail-1],que[tail-2])>play(j,que[tail-1])) tail--; 33 que[tail++]=j; 34 // cout<<f[cur][j]<<' '; 35 } 36 // cout<<endl; 37 } 38 printf("%d\n",m*f[cur][n]-sum[n]*sum[n]); 39 return 0; 40 }