征途 BZOJ 4518

征途

【问题描述】

Pine开始了从S地到T地的征途。

从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。

Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

帮助Pine求出最小方差是多少。

设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。

【输入格式】

第一行两个数 n、m。

第二行 n 个数,表示 n 段路的长度

【输出格式】

 一个数,最小方差乘以 m^2 后的

【样例输入】

5 2
1 2 5 8 6

【样例输出】

36

【数据范围】

1≤n≤3000,保证从 S 到 T 的总路程不超过 30000


题解:

来推一下式子:

方差:(x1 - aver)2 + (x2 - aver)+ ... + (xm - aver)2  / m

然后题意要求乘m2

那么

 m×[(x1 - aver)2 + (x2 - aver)+ ... + (xm - aver)]

= m×[x12 + x22 + ... + xm2 - 2aver(x+ x2 + ... + xm ) + m × aver2]

= m×(x12 + x22 + ... + xm2) - 2sum+ sum2  (aver = sum / m)

= m×(x12 + x22 + ... + xm2) - sum

其实m和sum都为常量,那么只要考虑中间的平方和部分

设f[i][j]为分到点j且分成i段时每一段的平方和

转移方程即为:f[i][j] = min(f[i][j], f[i - 1][k] + (sum[j] - sum[k]) * (sum[j] - sum[k])); (k < j)

三方效率肯定过不了,看出这是一个斜率优化的裸题,那就可以虾搞蛋了~\(≧▽≦)/~

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cstdio>
 6 #include<cmath>
 7 using namespace std;
 8 inline int Get()
 9 {
10     int x = 0;
11     char c = getchar();
12     while('0' > c || c > '9') c = getchar();
13     while('0' <= c && c <= '9')
14     {
15         x = (x << 3) + (x << 1) + c - '0';
16         c = getchar();
17     }
18     return x;
19 }
20 int n, m;
21 int t, w;
22 int c[10233];
23 int s[10233];
24 long long aver;
25 long long f[3233][3233];
26 long long sum[10233];
27 double Up(int x, int y, int i)
28 {
29     return f[i - 1][x] + sum[x] * sum[x] - f[i - 1][y] - sum[y] * sum[y];
30 }
31 double Down(int x, int y)
32 {
33     return (sum[x] - sum[y]) << 1;
34 }
35 long long Dp(int i, int j, int x)
36 {
37     return f[i - 1][x] + (sum[j] - sum[x]) * (sum[j] - sum[x]);
38 }
39 int main()
40 {
41     scanf("%d%d", &n, &m);
42     for(int i = 1; i <= m; ++i)
43         for(int j = 1; j <= n; ++j)
44             f[i][j] = 214748364721474836LL;
45     for(int i = 1; i <= n; ++i)
46     {
47         scanf("%d", &c[i]);
48         sum[i] = sum[i - 1] + c[i];
49         f[1][i] = sum[i] * sum[i];
50     }
51     aver = sum[n];
52     for(int i = 2; i <= m; ++i)
53     {
54         t = 1, w = 0;
55         s[++w] = i - 1;
56         for(int j = i; j <= n; ++j)
57         {
58 /*            
59             for(int k = i - 1; k <= j; ++k)
60             f[i][j] = min(f[i][j], f[i - 1][k] + (sum[j] - sum[k]) * (sum[j] - sum[k]));
61 */
62             while(t < w && Up(s[t], s[t + 1], i) / Down(s[t], s[t + 1]) <= sum[j]) ++t;
63             f[i][j] = Dp(i, j, s[t]);
64             while(t < w && Up(j, s[w], i) / Down(j, s[w]) <= Up(s[w], s[w - 1], i) / Down(s[w], s[w - 1])) --w;
65             s[++w] = j;
66         }
67     }
68     printf("%lld", (long long) m * f[m][n] - aver * aver);
69 }
posted @ 2017-01-04 11:35  草根柴鸡  阅读(155)  评论(0编辑  收藏  举报