P9338 [JOISC 2023 Day3] Chorus
感觉完全做不来这种题/ll.
首先发现
考虑一个神秘观察:把 A 表示向右一步,B 表示向上一步,显然一次操作只会是把一个上右改成右上。当然有解的一个必要条件是它必须在对角线下方,我们把在对角线上方的部分改到对角线下方,代价提前计算掉,现在只考虑路径在对角线之下的情况。
考虑在路径上做上面那个贪心,相当于每次沿着路径往右走,碰到一个向上就一直向上直到碰到对角线再向右,两次拐弯衔接的地方可能会平移一部分路径,但这个是合法的。
于是要求
设
让我们暂时忘掉决策单调性。考虑给
总时间复杂度
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
typedef long long LL;
constexpr int N = 2e6 + 5;
constexpr LL inf = 1e18;
int n, k, t[N], g[N]; LL cnt[N], sum[N], pre[N], f[N], ans, ns;
int q[N], hd, tl;
LL X(int i) { return i; }
LL Y(int i) {
return f[i] + pre[i - 1] + i;
}
bool chk(LL m) {
fill(f + 1, f + n + 2, inf);
fill(g + 1, g + n + 2, k + 1);
f[1] = g[1] = 0;
q[hd = tl = 1] = 1;
for (int i = 2; i <= n + 1; i++) {
while (hd < tl && i * (X(q[hd + 1]) - X(q[hd])) > (Y(q[hd + 1]) - Y(q[hd]))) hd++;
LL val = -X(q[hd]) * i + Y(q[hd]);
f[i] = val + (cnt[i - 1] + 1) * (i - 1) - sum[i - 1] - m, g[i] = g[q[hd]] + 1;
while (hd < tl && (Y(i) - Y(q[tl])) * (X(q[tl]) - X(q[tl - 1])) < (Y(q[tl]) - Y(q[tl - 1])) * (X(i) - X(q[tl]))) tl--;
q[++tl] = i;
}
ans = f[n + 1];
return g[n + 1] <= k;
}
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
string str;
cin >> str;
str = ' ' + str;
int cA = 0, cB = 0;
for (int i = 1; i <= 2 * n; i++) {
if (str[i] == 'A') ++cA;
else ++cB, t[cB] = cA;
}
for (int i = 1; i <= n; i++) if (t[i] < i) ns += i - t[i], t[i] = i;
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + t[i];
for (int i = 1; i <= n; i++) ++cnt[t[i]], sum[t[i]] += t[i];
for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1], sum[i] += sum[i - 1];
LL l = -5e11, r = 0, p = 0;
while (l <= r) {
LL m = (l + r) >> 1;
if (chk(m)) p = m, l = m + 1;
else r = m - 1;
}
chk(p);
cout << ns + ans + 1LL * p * k << "\n";
return 0;
}