• 征途 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 }
  • 相关阅读:
    正则表达式
    数组去重
    [WOJ4354] 蜀石经
    [NOI2002] 银河英雄传说
    [洛谷P2186] 小Z的栈函数
    [洛谷P2756]飞行员配对方案问题
    [洛谷P2071] 座位安排
    [洛谷P2417]课程
    [洛谷P1640] [SCOI2010]连续攻击游戏
    [洛谷P3512 [POI2010]PIL-Pilots]
  • 原文地址:https://www.cnblogs.com/lytccc/p/6248001.html
Copyright © 2020-2023  润新知