loading...

🤫

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}$

🤫 AC 🤫 记录 🤫

#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;
}

正解是平衡树,懒得写了。

posted @ 2026-10-15 12:05  goldspade  阅读(8)  评论(0)    收藏  举报