[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\n", f[now ^ 1][n]);
  ll ans = m * f[now ^ 1][n] - s[n] * s[n];
  printf("%lld\n", ans);
}

posted on 2017-03-20 19:53 蒟蒻konjac 阅读(...) 评论(...) 编辑 收藏

统计