CF730I Olympiad in Programming and Sports

想复杂了……

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

  • SA,流量 p,费用 0
  • SB,流量 s,费用 0
  • Ai[1,n],流量 1,费用 ai
  • Bi[1,n],流量 1,费用 bi
  • i[1,n]T,流量 1,费用 0

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

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

一种是 SC(A\ 或者\ B)iT,由于 CiiT 本来流量为 1,可以增广说明原本就没选。对应取一个没被选的 i 加进 C 集合的方案,那肯定是选 maxiAB{ai}

另一种是 SAiBjT(或者 SBiAjT,不难发现绕多几圈肯定不优),不难发现 iB 如果能流,代表原本 iB 集合里,现在被移动到了 A 集合。原本 j 也没有被选过。所以对应B 里面取一个 i 扔到 A,再从没选过的数里选一个扔进 B 的方案。由于对答案的贡献为 aibi+bj,所以肯定是取 maxiB{aibi}+maxjAB{bj}A 扔到 B 的情况类似。

所以只需要维护 4 种情况,开 4 个堆,分别维护还没被选的数中最大 ai、还没被选数中最大 biB 集合中最大 aibi 以及 A 集合中最大 biai 即可。

复杂度 O(nlogn)。代码很好写。

#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 @   Arghariza  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩