Luogu P6246 [IOI2000] 邮局 加强版

Luogu P6246 [IOI2000] 邮局 加强版

​ 设 \(f_{i,j}\) 表示前 \(i\) 个村庄放置了 \(j\) 个邮局的最小代价。则DP转移为 \(f_{i,j}=\min\limits_{k<i}\{f_{k,j-1}+\operatorname{calc}(k,i)\}\) 其中 \(\operatorname{calc}(l,r)\) 表示在区间 \((l,r]\) 中放置一个邮局后该区间内的最小花费。显然这个邮局应该放在最中间的位置,因此 \(\operatorname{calc}(l,r)\) 可以通过预处理 \(\mathcal O(1)\) 计算。于是我们得到了一个 \(\mathcal O(V^2P)\) 的做法。

​ 考虑优化。我们发现 \(\operatorname{calc}(l,r)\) 满足四边形不等式,因此 \(f_{i,j}\) 具有决策单调性。这是一个 \(\texttt{2D/1D}\) 转移,因此我们可以使用决策单调性分治优化转移,时间复杂度为 \(\mathcal O(VP\log V)\)

​ 由于 \(f_{i,j}\) 关于 \(i\) 具有决策单调性,因此 \(f_{V,j}\) 关于 \(j\) 是上凸的,于是我们可以使用wqs二分。这样转移式就变成了 \(f_{i}=\min\limits_{k<i}\{f_{k}+\operatorname{calc}(k,i)\}-K\),其中 \(K\) 为wqs二分的斜率。由于贡献函数 \(\operatorname{calc}(l,r)-K\) 依然满足四边形不等式,因此这个转移依然有决策单调性。这是一个 \(\texttt{1D/1D}\) 的转移,因此可以考虑使用二分队列优化。于是时间复杂度就是 \(\mathcal O(V\log V\log P)\)

参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int Maxn = 5e5 + 5;
int n, m;
int64_t a[Maxn], s[Maxn];
inline int64_t calc(int l, int r) { // (l, r]
  const int h = (l + r + 1) / 2;
  return s[l] + s[r] - 2 * s[h] + (2 * h - l - r) * a[h];
} // calc
pair<int64_t, int> check(int64_t K) {
  struct node {
    int t, l, r;
    node() = default;
    node(int t, int l, int r) : t(t), l(l), r(r) { }
  }; static deque<node> q; q.clear();
  static int64_t f[Maxn]; memset(f, 0, sizeof(f));
  static int fc[Maxn]; memset(fc, 0, sizeof(fc));
  auto trans = [&](int p, int x) { return f[p] + calc(p, x); };
  auto cmp_trans = [&](int px, int py, int x) {
    return pair(trans(px, x), fc[px]) <= pair(trans(py, x), fc[py]);
  }; // check()::[cmp_trans]
  q.emplace_back(0, 1, n); tie(f[0], fc[0]) = tuple(0, 0);
  for (int i = 1, j; i <= n; ++i) {
    if (q.front().r == i - 1) q.pop_front(); q.front().l = i;
    tie(f[i], fc[i]) = tuple(trans(q.front().t, i) - K, fc[q.front().t] + 1);
    for (j = n + 1; !q.empty() && cmp_trans(i, q.back().t, q.back().l); q.pop_back()) j = q.back().l;
    if (q.empty()) { q.emplace_back(i, i + 1, n); continue; }
    if (!cmp_trans(i, q.back().t, q.back().r)) { if (j <= n) q.emplace_back(i, j, n); continue; }
    int low = q.back().l, high = q.back().r, t = high;
    while (low <= high) {
      int mid = low + (high - low) / 2;
      if (cmp_trans(i, q.back().t, mid))
        high = mid - 1, t = mid;
      else low = mid + 1;
    } q.back().r = t - 1;
    q.emplace_back(i, t, n);
  } return pair(f[n], fc[n]);
} // check
int main(void) {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];
  int64_t low = -5e11, high = 5e11;
  int64_t ans = low - 1;
  while (low <= high) {
    int64_t mid = low + (high - low) / 2;
    if (auto [f, c] = check(mid); c <= m)
      ans = mid, low = mid + 1;
    else high = mid - 1;
  }
  auto [f, c] = check(ans);
  printf("%lld\n", f + m * ans);
  exit(EXIT_SUCCESS);
} // main
posted @ 2022-03-22 21:47  cutx64  阅读(22)  评论(0)    收藏  举报