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 }
View Code

 

posted @ 2017-10-12 16:10  Blue233333  阅读(142)  评论(0编辑  收藏  举报