题意
LOJ #6384. 「是男人就过8题——Pony.ai」SignLocation
给定 \(n\) 个整点 \(p_1, ..., p_n\) 以及 \(k\) 次标记点的机会,定义 \(c(i, j)\) 为:
-
第 \(i\) 个整点和第 \(j\) 个整点之间存在标记点,则 \(c(i, j)\) 为距离 \(i\) 最近的标记点与 \(i\) 的坐标之差。
-
反之为 \(|p_i - p_j|\)
注意标记点不一定在整点上。
求 \(\sum\limits_{i \neq j} c(i, j)\) 的最小值。
\(0 \leq k \leq 200, 1 \leq n \leq 10000, p_i \leq 10^7\)
思路
决策单调性优化 dp.
首先可以用反证法证明标记整点最优。
于是可以考虑设 \(f[i][j]\) 表示已经标记 \(i\) 个整点,最后一个标记的是第 \(j\) 个整点时已经产生的最小贡献。
容易发现选出的 \(k\) 个标记点会将值域分成 \(k + 1\) 个区间(可能为空),不妨令 \(\sum\limits_{i \neq j} c(i, j)\) 算作是 \(i\) 所在区间的贡献。
假设当前最后一个标记点在 \(r\),倒数第二个标记点在 \(l\),那么转移时只需要考虑区间 \((l, r)\) 的贡献。
分类讨论推一下式子:
-
\(l < j < r\)
此时 \((i, j)\) 之间没有其他的标记点,于是贡献为坐标之差。
\(\sum\limits_{i \neq j} c(i, j) = 2 \sum\limits_{p = l + 2}^{r - 1} \sum\limits_{q = l + 1}^{r - 1} (x_p - x_q)\)
考察每一个下标的出现次数,可以化简上式得:
\(原式 = \sum\limits_{p = l + 2}^{r - 1} x_p (p - l - 1) - \sum\limits_{q = l + 1}^{r - 2} x_q (r - q - 1)\) -
\(j \leq l\)
此时距离 \(i\) 最近的标记点为 \(l\)
\(\sum\limits_{i \neq j} c(i, j) = \sum\limits_{p = l + 1}^{r - 1} (x_p - x_l)l\)
同理化简得 \(原式 = (\sum\limits_{p = l + 1}^{r - 1} x_p l) - x_l (r - l - 1) l\) -
\(j \geq r\)
此时距离 \(i\) 最近的标记点为 \(r\)
\(\sum\limits_{i \neq j} c(i, j) = \sum\limits_{p = l + 1}^{r - 1} (x_r - x_p)(n - r + 1)\)
化简得 \(原式 = x_r (r - l - 1) (n - r + 1) - \sum\limits_{p = l + 1}^{r - 1} x_p(n - r + 1)\)
发现只需处理 \(p_i\) 和 \(i p_i\) 的前缀和就可以 \(O(1)\) 计算一段区间的贡献。
观察做法,发现是一个划分区间的 dp(选点等价于划分区间),于是联想到决策单调性优化 dp.
根据题解知 确实有决策单调性(具体不太会证),于是上单调性分治优化即可,时间复杂度 \(O(k n \log n)\)
代码
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 5;
const int maxk = 2e2 + 5;
const ll inf = 1e18;
int n, k;
int p[maxn];
ll s1[maxn], s2[maxn];
ll f[maxk][maxn];
ll get1(int l, int r)
{
ll ans = 2 * (s2[r - 1] - s2[l + 1] + s2[r - 2] - s2[l] - (s1[r - 1] - s1[l + 1]) * (l + 1) - (s1[r - 2] - s1[l]) * (r - 1));
ans += 1ll * l * (s1[r - 1] - s1[l] - 1ll * p[l] * (r - l - 1));
ans += 1ll * (n - r + 1) * (1ll * p[r] * (r - l - 1) - s1[r - 1] + s1[l]);
return ans;
}
ll get2(int l, int r)
{
if (l == r) return 0;
ll ans = 2ll * (s2[r] - s2[l + 1] + s2[r - 1] - s2[l] - (s1[r] - s1[l + 1]) * (l + 1) - (s1[r - 1] - s1[l]) * r);
ans += 1ll * l * (s1[r] - s1[l] - 1ll * p[l] * (r - l));
return ans;
}
void solve(int cur, int l, int r, int vl, int vr)
{
if (l > r) return;
int mid = (l + r) >> 1, pos = l;
ll res = inf;
for (int i = min(vr, mid - 1); i >= vl; i--)
{
ll cost = f[cur - 1][i] + get1(i, mid);
if (cost < res) res = cost, pos = i;
}
f[cur][mid] = res;
solve(cur, l, mid - 1, vl, pos);
solve(cur, mid + 1, r, pos, vr);
}
int main()
{
while (~scanf("%d%d", &n, &k))
{
k = min(k, n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &p[i]);
s1[i] = s1[i - 1] + p[i];
s2[i] = s2[i - 1] + 1ll * p[i] * i;
}
if (k == 0)
{
ll ans = 0;
for (int i = 1; i <= n; i++) ans += (1ll * p[i] * i - s1[i]);
printf("%lld\n", (ans << 1ll));
continue;
}
for (int i = 1; i <= k; i++)
{
if (i == 1) solve(1, 1, n, 0, 0);
else solve(i, i, n, i - 1, n - 1);
}
ll ans = inf;
for (int i = k; i <= n; i++) ans = min(ans, f[k][i] + get2(i, n));
printf("%lld\n", ans);
}
return 0;
}
标签:SignLocation,ll,Pony,limits,int,题解,s1,s2,sum
From: https://www.cnblogs.com/lingspace/p/loj-6384.html