🤫
Two Characters, Two Colors
给定 \(0/1\) 字符串 \(s\),如果保留第 \(i\) 个位置,你将获得 \(r_i\) 的金币;否则你将获得 \(b_i\) 的金币。最后把所有保留下来的字符按原顺序拼接,求逆序对数 \(d\),并扣除 \(d\) 的金币。求最后金币数的最大值。
\(n \le 4 \cdot 10^5\)。
首先做一步转化,如果 \(r_i \le b_i\),由于留下来一定不优,我们会直接选择不留下。把这些位置去掉后剩下的全是 \(r_i > b_i\)。
每个位置只有保留/不保留两种可能性,考虑最小割建模。设超级源汇点 \(S, T\),假定如果 [被划分到 \(S\) 侧] xor [\(s_i = 1\)] 为 \(0\) 则保留,为 \(1\) 则不保留。建边是简单的,令 \(w_i = r_i - b_i\):
- 若 \(s_i=1\) 连接 \(S \xrightarrow{w_i} i\),否则连 \(i \xrightarrow{w_i}T\)。
- 若 \(i < j \land s_i = 1 \land s_j = 0\),连接 \(i \xrightarrow{1} j\)。
建边复杂度瓶颈在第二类边,个数为 \(n ^ 2\) 无法通过。不过它有一些良好的性质:左侧点只会对一段后缀连边,边权均为 \(1\)。
实际上,不需要跑网络流,贪心就可解决此问题。因为左侧点只会连一段后缀,相应的,越靠后的右侧点就有更多选择权(靠前的点流量源集合完全被靠后的点流量源集合包含)。所以从前往后枚举右侧点(\(s_i = 0\) 的 \(j\)),并尽量选择当前剩余容量更大的 \(i\)(这样能够最大化可选流量源集合),选剩余容量前 \(w_j\) 大的即可。
接下来的问题是,维护流量源集合,支持前 \(k\) 大整体做减法,vector 维护即可。
$\texttt{how to use vector<LL> fhq to solve this problem}$
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define drep(i, r, l) for (int i = r; i >= l; --i)
#define LL long long
#define FRE(p) freopen(#p".in", "r", stdin), freopen(#p".out", "w", stdout)
#define FASTIO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define eb emplace_back
#define PII pair<int,int>
#define fi first
#define se second
using namespace std;
constexpr int N = 4e5+5, P = 1e9+7;
int n;
string s;
LL r[N], b[N];
vector<LL> fhq;
void modify(int tmp) {
for (int j = 0; j < tmp; ++j) --fhq[j];
if (tmp < fhq.size() && fhq[tmp - 1] < fhq[tmp]) inplace_merge(fhq.begin(), fhq.begin()+tmp, fhq.end(), greater<>());
while (fhq.size() && !fhq.back()) fhq.pop_back();
}
inline void solve() {
cin >> n >> s;
rep(i, 1, n) cin >> r[i];
rep(i, 1, n) cin >> b[i];
LL ans = 0;
int cnt0 = 0, cnt1 = 0;
rep(i, 1, n) cnt0 += r[i] > b[i] && s[i - 1] == '0', cnt1 += r[i] > b[i] && s[i - 1] == '1';
if (cnt0 > cnt1) {
rep(i, 1, n) {
LL w = min(r[i]-b[i], static_cast<LL>(n));
if (b[i] >= r[i]) ans += b[i];
else {
if (s[i-1] == '1') fhq.insert(lower_bound(fhq.begin(), fhq.end(), w, greater<>()), w), ans += r[i];
else {
LL tmp = min(static_cast<LL>(fhq.size()), r[i] - b[i]);
ans += r[i] - tmp;
modify(tmp);
}
}
}
} else {
drep(i, n, 1) {
LL w = min(r[i]-b[i], static_cast<LL>(n));
if (b[i] >= r[i]) ans += b[i];
else {
if (s[i-1] == '0') fhq.insert(lower_bound(fhq.begin(), fhq.end(), w, greater<>()), w), ans += r[i];
else {
LL tmp = min(static_cast<LL>(fhq.size()), r[i] - b[i]);
ans += r[i] - tmp;
modify(tmp);
}
}
}
}
fhq.clear();
cout << ans << '\n';
}
signed main() {
FASTIO;
int _ = 1; cin >> _;
while (_--) solve();
return 0;
}
正解是平衡树,懒得写了。

浙公网安备 33010602011771号