CodeForces 1895G Two Characters, Two Colors

发布时间 2023-11-16 19:49:27作者: zltzlt

洛谷传送门

CF 传送门

要求最大化收益加上支出,又因为每个字符有染红和染蓝两种选择,考虑最小割模型。可以看成是一开始先获得 \(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;
}