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
浙公网安备 33010602011771号