CodeForces 1895G Two Characters, Two Colors
要求最大化收益加上支出,又因为每个字符有染红和染蓝两种选择,考虑最小割模型。可以看成是一开始先获得 \(r_i + b_i\) 的收益,然后对于每个 \(0\),连边 \((S, i, b_i), (i, T, r_i)\);对于每个 \(1\),连边 \((S, i, r_i), (i, T, b_i)\);每个 \(1\) 向它后面的所有 \(0\) 连容量为 \(1\) 的边。然后 \(\sum r_i + b_i\) 减去最小割即为答案。
很可惜边数是 \(O(n^2)\) 的,没办法直接跑最大流。观察这个网络流图,发现它长得比较好看,于是可以尝试模拟最大流。
我们首先有一个显然的流量下界 \(\sum \min(r_i, b_i)\)。我们先让每个点流过 \(\min(r_i, b_i)\) 的流量。对于一个 \(1\),源点流向它的 \(k_i = r_i - \min(r_i, b_i)\) 的流量被浪费了。我们尝试把这些流量往后面的 \(0\) 流。那么对于一个 \(0\),它还能往汇点流 \(k_i\) 的流量。要从前面的 \(1\) 获取一些流量。我们贪心地在前面剩余流量前 \(k_i\) 大的 \(1\) 获取 \(1\) 的流量,正确性证明大概就是选择其他的 \(1\) 获取流量不会变优。
于是我们遇到一个 \(1\) 就往剩余流量可重集里加入 \(k_i\),遇到一个 \(0\) 就把这个集合的前 \(k_i\) 大元素减 \(1\),然后剔除掉值为 \(0\) 的元素。多的流量就是每次遇到 \(0\) 时,集合内实际被减了 \(1\) 的元素数量之和。
那么现在需要一个数据结构,支持:
- 插入元素;
- 把前 \(k\) 大元素减 \(1\);
- 剔除值为 \(0\) 的元素。
使用 FHQ Treap 维护即可。前 \(k\) 大元素减 \(1\) 的操作,可以先分裂成 \(x, y\) 两棵子树使得 \(x\) 大小为 \(sz_{rt} - k\),\(y\) 大小为 \(k\);然后如果 \(x\) 子树内最大值小于 \(y\) 子树内最小值,就可以直接打个 tag 把 \(y\) 子树内的全部值减 \(1\);否则继续分裂,设此时 \(x\) 子树内最大值为 \(t\),把 \(x\) 分成值 \(< t\) 和值 \(= t\) 两部分,\(y\) 分成值 \(= t\) 和值 \(> t\) 两部分,把 \(y\) 的两部分分别打 tag 减 \(1\) 后再依次合并即可。
时间复杂度 \(O(n \log n)\)。
code
// Problem: G. Two Characters, Two Colors
// Contest: Codeforces - Educational Codeforces Round 157 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1895/G
// Memory Limit: 512 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline ll read() {
char c = getchar();
ll x = 0;
bool f = 0;
for (; !isdigit(c); c = getchar()) f |= (c == '-');
for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return f ? -x : x;
}
inline void reads(char *s) {
int len = 0;
char c = getchar();
while (isspace(c)) {
c = getchar();
}
while (!isspace(c)) {
s[len++] = c;
c = getchar();
}
}
const int maxn = 400100;
ll n, a[maxn], b[maxn];
char s[maxn];
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
namespace FHQ {
int ntot, rt, ls[maxn], rs[maxn];
ll p[maxn], val[maxn], tag[maxn], sz[maxn];
inline void init() {
for (int i = 1; i <= ntot; ++i) {
ls[i] = rs[i] = p[i] = val[i] = tag[i] = 0;
}
ntot = rt = 0;
}
inline void pushup(int x) {
sz[x] = sz[ls[x]] + sz[rs[x]] + 1;
}
inline void pushtag(int x, ll y) {
if (x) {
val[x] += y;
tag[x] += y;
}
}
inline void pushdown(int x) {
if (!tag[x]) {
return;
}
pushtag(ls[x], tag[x]);
pushtag(rs[x], tag[x]);
tag[x] = 0;
}
inline int newnode(ll x) {
int u = ++ntot;
p[u] = rnd();
val[u] = x;
sz[u] = 1;
return u;
}
void split(int u, ll k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
pushdown(u);
if (val[u] <= k) {
x = u;
split(rs[u], k, rs[u], y);
} else {
y = u;
split(ls[u], k, x, ls[u]);
}
pushup(u);
}
void split2(int u, ll k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
pushdown(u);
if (sz[ls[u]] + 1 <= k) {
x = u;
split2(rs[u], k - sz[ls[u]] - 1, rs[u], y);
} else {
y = u;
split2(ls[u], k, x, ls[u]);
}
pushup(u);
}
int merge(int x, int y) {
if (!x || !y) {
return x + y;
}
pushdown(x);
pushdown(y);
if (p[x] < p[y]) {
rs[x] = merge(rs[x], y);
pushup(x);
return x;
} else {
ls[y] = merge(x, ls[y]);
pushup(y);
return y;
}
}
inline void delzero() {
int x, y;
split(rt, 0, x, y);
rt = y;
}
inline ll qmin(int x) {
while (ls[x]) {
pushdown(x);
x = ls[x];
}
return val[x];
}
inline ll qmax(int x) {
while (rs[x]) {
pushdown(x);
x = rs[x];
}
return val[x];
}
inline void insert(ll k) {
int x, y;
split(rt, k, x, y);
rt = merge(merge(x, newnode(k)), y);
}
inline ll delkmax(ll k) {
k = min(k, sz[rt]);
if (!k) {
return 0;
}
int x, y;
split2(rt, sz[rt] - k, x, y);
if (qmax(x) < qmin(y)) {
pushtag(y, -1);
rt = merge(x, y);
} else {
ll t = qmax(x);
int u1, v1, u2, v2;
split(x, t - 1, u1, v1);
split(y, t, u2, v2);
pushtag(u2, -1);
pushtag(v2, -1);
rt = merge(merge(u1, u2), merge(v1, v2));
}
return k;
}
}
void solve() {
n = read();
reads(s + 1);
ll res = 0, ans = 0;
for (int i = 1; i <= n; ++i) {
a[i] = read();
res += a[i];
}
for (int i = 1; i <= n; ++i) {
b[i] = read();
res += b[i];
ans += min(a[i], b[i]);
}
FHQ::init();
for (int i = 1; i <= n; ++i) {
ll k = a[i] - min(a[i], b[i]);
if (s[i] == '0') {
ans += FHQ::delkmax(k);
} else {
FHQ::insert(k);
}
FHQ::delzero();
}
printf("%lld\n", res - ans);
}
int main() {
int T = read();
while (T--) {
solve();
}
return 0;
}