先设 \(n=x+y+z\)。
首先将三元组 \((a,b,c)\) 替换成二元组 \((e=b-a,f=c-a)\)。即先默认所有人拿金币。
然后问题转换为在 \(n\) 个二元组中选 \(y\) 个获得 \(e\) 收益,选 \(z\) 个获得 \(f\) 收益,最大化总收益。
对于两个二元组 \((e_i,f_i),(e_j,f_j)\),假设 \(i\) 选 \(f\),\(j\) 选 \(e\)。考虑什么时候交换他们的选择收益不会变少。即 \(e_i+f_j\ge e_j+f_i\),移项得 \(e_i-f_i\ge e_j-f_j\),那么通过排序使得二元组按 \(e-f\) 单调不降。就有对于任意 \(i\lt j\),如果 \(i\) 选 \(f\),\(j\) 选 \(e\),那么交换两者状态一定不会变差。
换言之,存在最优解满足选 \(e\) 的全在选 \(f\) 的左边。也就是说存在一个 \(k\),使得 \(1\sim k\) 中选 \(y\) 个 \(e\),\(k+1\sim n\) 中选 \(z\) 个 \(f\)。
这个就可以通过优先队列处理出每个前缀前 \(y\) 大的 \(e\) 的和以及每个后缀前 \(z\) 大的 \(f\) 的和。
最后枚举 \(k\) 即可得出答案。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
int x, y, z, n;
struct node {
int b, c;
} p[N];
ll sum, mx = -1e18;
ll L[N], R[N];
priority_queue<int, vector<int>, greater<int> > q;
int main() {
scanf("%d%d%d", &x, &y, &z), n = x + y + z;
for (int i = 1, a, b, c; i <= n; ++i) {
scanf("%d%d%d", &a, &b, &c);
sum += a, p[i].b = b - a, p[i].c = c - a;
}
sort(p + 1, p + n + 1, [](node x, node y) {
return x.b - x.c > y.b - y.c;
});
for (int i = 1; i <= n; ++i) {
L[i] = L[i - 1] + p[i].b;
q.push(p[i].b);
if (q.size() > y) {
L[i] -= q.top();
q.pop();
}
}
while (!q.empty()) q.pop();
for (int i = n; i; --i) {
R[i] = R[i + 1] + p[i].c;
q.push(p[i].c);
if (q.size() > z) {
R[i] -= q.top();
q.pop();
}
}
for (int i = y; i <= n - z; ++i) mx = max(mx, L[i] + R[i + 1]);
printf("%lld", sum + mx);
return 0;
}
标签:二元,int,ll,d%,中选,pop,AGC018C
From: https://www.cnblogs.com/Kobe303/p/16825083.html