CF730I Olympiad in Programming and Sports

想复杂了……

这种分到两边的问题,考虑建立费用流模型,建立两个点 \(A,B\) 表示分到 \(A\) 的数或者分到 \(B\) 的数:

  • \(S\to A\),流量 \(p\),费用 \(0\)
  • \(S\to B\),流量 \(s\),费用 \(0\)
  • \(A\to i\in[1,n]\),流量 \(1\),费用 \(a_i\)
  • \(B\to i\in [1,n]\),流量 \(1\),费用 \(b_i\)
  • \(i\in[1,n]\to T\),流量 \(1\),费用 \(0\)

建图方式很好理解,跑费用流(最大费用)后如果 \(A\to i\) 的边流量为 \(0\) 就代表选到了 \(A\)\(B\) 同理。

然后考虑每次增广的过程,有 \(2\) 种情况:

一种是 \(S\to C(A\text{\ 或者\ }B)\to i\to T\),由于 \(C\to i\)\(i\to T\) 本来流量为 \(1\),可以增广说明原本就没选。对应取一个没被选的 \(i\) 加进 \(C\) 集合的方案,那肯定是选 \(\max\limits_{i\notin A\cup B}\{a_i\}\)

另一种是 \(S\to A\to i\to B\to j\to T\)(或者 \(S\to B\to i\to A\to j\to T\),不难发现绕多几圈肯定不优),不难发现 \(i\to B\) 如果能流,代表原本 \(i\)\(B\) 集合里,现在被移动到了 \(A\) 集合。原本 \(j\) 也没有被选过。所以对应\(B\) 里面取一个 \(i\) 扔到 \(A\),再从没选过的数里选一个扔进 \(B\) 的方案。由于对答案的贡献为 \(a_i-b_i+b_j\),所以肯定是取 \(\max\limits_{i\in B}\{a_i-b_i\}+\max\limits_{j\notin A\cup B}\{b_j\}\)\(A\) 扔到 \(B\) 的情况类似。

所以只需要维护 \(4\) 种情况,开 \(4\) 个堆,分别维护还没被选的数中最大 \(a_i\)、还没被选数中最大 \(b_i\)\(B\) 集合中最大 \(a_i-b_i\) 以及 \(A\) 集合中最大 \(b_i-a_i\) 即可。

复杂度 \(O(n\log n)\)。代码很好写。

#include <bits/stdc++.h>
using namespace std;

namespace vbzIO {
	char ibuf[(1 << 20) + 1], *iS, *iT;
	#if ONLINE_JUDGE
	#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
	#else
	#define gh() getchar()
	#endif
	#define rd read
	#define wr write
	#define pc putchar
	#define pi pair<int, int>
	#define mp make_pair
	#define fi first
	#define se second
	#define pb push_back
	#define ins insert
	#define era erase
	inline int read () {
		char ch = gh();
		int x = 0;
		bool t = 0;
		while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
		while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
		return t ? ~(x - 1) : x;
	}
	inline void write(int x) {
		if (x < 0) {
			x = ~(x - 1);
			putchar('-');
		}
		if (x > 9)
			write(x / 10);
		putchar(x % 10 + '0');
	}
}
using vbzIO::read;
using vbzIO::write;

const int N = 3e3 + 300;
int n, ans, p1, p2, a[N], b[N], in[N];
priority_queue<pi> q0, q1, q2, q3;

int main() {
	n = rd(), p1 = rd(), p2 = rd();
	for (int i = 1; i <= n; i++) a[i] = rd(), q0.push(mp(a[i], i));
	for (int i = 1; i <= n; i++) b[i] = rd(), q1.push(mp(b[i], i));
	while (p1 || p2) {
		int d = -1e9, op = -1;
		while (!q0.empty() && in[q0.top().se]) q0.pop();
		while (!q1.empty() && in[q1.top().se]) q1.pop();
		while (!q2.empty() && in[q2.top().se] != 2) q2.pop();
		while (!q3.empty() && in[q3.top().se] != 1) q3.pop();
		if (p1 && !q0.empty()) if (q0.top().fi > d) d = q0.top().fi, op = 0;
		if (p2 && !q1.empty()) if (q1.top().fi > d) d = q1.top().fi, op = 1;
		if (p1 && !q2.empty() && !q1.empty()) if (q2.top().fi + q1.top().fi > d) d = q2.top().fi + q1.top().fi, op = 2;
		if (p2 && !q3.empty() && !q0.empty()) if (q3.top().fi + q0.top().fi > d) d = q3.top().fi + q0.top().fi, op = 3;
		if (!op) {
			auto tp = q0.top(); q0.pop();
			p1--, ans += d, in[tp.se] = 1;
			q3.push(mp(b[tp.se] - a[tp.se], tp.se));
		} else if (op == 1) {
			auto tp = q1.top(); q1.pop();
			p2--, ans += d, in[tp.se] = 2;
			q2.push(mp(a[tp.se] - b[tp.se], tp.se));
		} else if (op == 2) {
			auto t1 = q2.top(), t2 = q1.top(); q2.pop(), q1.pop();
			p1--, ans += d, in[t2.se] = 2, in[t1.se] = 1;
			q2.push(mp(a[t2.se] - b[t2.se], t2.se));
			q3.push(mp(b[t1.se] - a[t1.se], t1.se));
		} else if (op == 3) {
			auto t1 = q3.top(), t2 = q0.top(); q3.pop(), q0.pop();
			p2--, ans += d, in[t2.se] = 1, in[t1.se] = 2;
			q2.push(mp(a[t1.se] - b[t1.se], t1.se));
			q3.push(mp(b[t2.se] - a[t2.se], t2.se));
		} else break;
	}
	wr(ans), puts("");
	for (int i = 1; i <= n; i++)
		if (in[i] == 1) wr(i), pc(' ');
	puts("");
	for (int i = 1; i <= n; i++)
		if (in[i] == 2) wr(i), pc(' ');
	return 0;
}

posted @ 2023-07-20 18:34  Ender_32k  阅读(9)  评论(0编辑  收藏  举报