:::info[Description] Link. :::
:::infoSolution 题目要求把第 个 A 和第 个 B 分到同一首歌曲了,因此可以看作是把 划分成恰好 个连续段。记 表示把 区间划分为完整的一段的最小操作数是多少,则考虑记 表示第 个 A 的前面有多少个 B。为了让这一段合法,对每个 都需要让第 个 A 跨过 个 B。因此得出结论 。
考虑一个暴力的 dp。设 表示考虑前 个位置,划分恰好 段所需要的最小交换次数。直接转移可以枚举决策点 ,转移式形如 的形式。
观察 可以发现 函数构成的矩阵是 Monge 矩阵,因此 dp 数组关于划分的段的数量 是凸的,外层可以用 wqs 二分优化去掉段数 这个维度,。因为 单调不下降,因此考虑维护 表示 后面第一个满足 的位置, 为 的前缀和信息,则显然有 ,代入 dp 转移式后可得 ,用李超线段树优化 dp 转移即可做到 求解问题。
但是 看着就不是什么人类,因此被迫学习了斜率优化 dp 的线性做法。 斜率优化太难了,还是写 2log 了。 最后还是写的单 做法。
:::
:::success[Code]
namespace lowspeed_song {
inline void init() {
}
int n, k;
char s[N];
int a[N], cnt, idx, pre[N], p[N], f[N], g[N], q[N];
inline int calc(int x) { return g[x] - pre[p[x] - 1] + x * (p[x] - 1); }
inline pair<int, int> chk(int x) {
int head = 0, tail = 0;
for (int i = 1; i <= n; ++i) {
while (head < tail && calc(q[head + 1]) - calc(q[head]) < (q[head + 1] - q[head]) * i) ++head;
f[i] = f[q[head]] + 1;
g[i] = g[q[head]] + pre[i] - pre[p[q[head]] - 1] - q[head] * (i - p[q[head]] + 1) + x;
while (head < tail && (calc(q[tail]) - calc(q[tail - 1])) * (i - q[tail]) > (calc(i) - calc(q[tail])) * (q[tail] - q[tail - 1])) --tail;
q[++tail] = i;
} return {f[n], g[n]};
}
inline void sol() {
cin >> n >> k;
scanf("%s", s + 1);
for (int i = 1; i <= n + n; ++i) {
if (s[i] == 'A') a[++idx] = cnt, pre[idx] = pre[idx - 1] + a[idx];
else ++cnt;
}
for (int i = 1; i <= n; ++i) p[i] = max<int>(lower_bound(a + 1, a + n + 1, i) - a, i + 1);
int l = 0, r = 1e12, best = r + 1;
while (l <= r) {
int mid = l + r >> 1;
chk(mid).first <= k ? (best = mid, r = mid - 1) : (l = mid + 1);
} cout << chk(best).second - k * best << '\n';
}
} // namespace lowspeed_song:::
难度:黑-
鲜活指数:8
喜欢的话,留下你的评论吧~