zoukankan      html  css  js  c++  java
  • [bzoj4518][Sdoi2016]征途-斜率优化

    Brief Description

    Pine开始了从S地到T地的征途。
    从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
    Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
    Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
    帮助Pine求出最小方差是多少。
    设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。

    Algorithm Design

    不难设计出DP方程,

    [f(i, j) = f(i-1, k) + w(j,k) ]

    [w(j, k)=f(i-1,k)+(s[k]-s[j])^2 ]

    很容易得到斜率优化的式子:

    [frac{f(i-1,j)-f(i-1,k)+s_j^2-s_k^2}{2(s_j-s_k)}leqslant s_i ]

    然后乱搞就好辣

    Notice

    注意特判分母为0
    QAQ
    另外吐嘈一下,这个题写(O(n^3))的暴力居然有60分!!!

    Code

    #include <algorithm>
    #include <cctype>
    #include <cstdio>
    #define ll long long
    const ll inf = 99999999999;
    const int maxn = 60010;
    // const int maxm = 30010;
    ll f[2][maxn];
    int n, m, now = 1;
    ll s[maxn];
    ll sq(ll x) { return x * x; };
    ll read() {
      int x = 0, f = 1;
      char ch = getchar();
      while (!isdigit(ch)) {
        if (ch == '-')
          f = -1;
        ch = getchar();
      }
      while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    inline double calck(int i, int j) {
      if (s[j] - s[i] == 0)
        return inf;
      return (f[now ^ 1][j] - f[now ^ 1][i] + sq(s[j]) - sq(s[i])) /
             ((s[j] - s[i]) * 2);
    }
    int que[maxn], head, size, tail;
    int main() {
      /*  freopen("menci_journey.in", "r", stdin);
        freopen("menci_journey.out", "w", stdout); */
      n = read();
      m = read();
      for (int i = 1; i <= n; i++)
        s[i] = read();
      for (int i = 1; i <= n; i++)
        s[i] += s[i - 1];
      for (int i = 1; i <= n; i++)
        f[0][i] = inf;
      for (int i = 1; i <= m; i++) {
        head = tail = 0;
        size = 1;
        for (int j = 1; j <= n; j++) {
          while (size >= 2) {
            int a = que[head];
            int b = que[head + 1];
            if (calck(a, b) < s[j]) {
              head++;
              size--;
              if (size < 2)
                break;
            } else
              break;
          }
          int k = que[head];
          f[now][j] = f[now ^ 1][k] + sq(s[j] - s[k]);
          if (size >= 2) {
            int x = que[tail];
            int y = que[tail - 1];
            while (calck(y, x) > calck(x, j)) {
              tail--;
              size--;
              if (size < 2)
                break;
              x = que[tail];
              y = que[tail - 1];
            }
          }
          que[++tail] = j;
          size++;
        }
        now ^= 1;
      }
      // printf("%lld
    ", f[now ^ 1][n]);
      ll ans = m * f[now ^ 1][n] - s[n] * s[n];
      printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    动态规划小练
    组合计数小练
    【WC2019】 通道
    【PKUSC2018】主斗地
    【NOI2009】诗人小G
    【THUWC 2017】随机二分图
    【NOI2017】游戏与2-sat方案输出
    Codeforces 1109D sasha and interesting fact from graph theory
    Codeforces 1152E neko and flashback
    ZJOI2019游记
  • 原文地址:https://www.cnblogs.com/gengchen/p/6591241.html
Copyright © 2011-2022 走看看