题解:P12360 [eJOI 2024] 足球决斗 / CF Duels
思路
首先我们明白,未被看到的体育场完全可以当作最劣情况考虑。那么最劣的情况奖金数量和对方的技能等级顺序排列。遂我们可以构造出看完前
- 对于前
k 个球场,p 和b 不变; - 对于后
n-k 个球场,p 和b 皆顺序排序。
随后考虑我方如何获得最大奖金。枚举我方能力小的到能力大的,用优先队列存储第
具体做法就是二分至多能看完的体育场数量
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, p[500005], a[500005], b[500005], x[500005], y[500005], l, r, sum, ans;
struct node {
int p, b;
} c[500005];
bool cmp(node x, node y) {
return x.b < y.b;
}
priority_queue<int, vector<int>, less<int>> q;//较大的元素会被优先取出
bool check(int k) {
int res = 0;
while (!q.empty()) q.pop();
for (int i = 1; i <= n; i++) {
x[i] = p[i];
y[i] = b[i];
}
sort(x + k + 1, x + n + 1);
sort(y + k + 1, y + n + 1);
for (int i = 1; i <= n; i++) {
c[i] = {x[i], y[i]};
}
sort(c + 1, c + n + 1, cmp);
for (int i = 1, j = 1; i <= n; i++) {
while (c[j].b < a[i] && j != n + 1) {
q.push(c[j++].p);//把所有打得过的放入优先队列
}
if (!q.empty()) {
res += q.top();
q.pop();
}//若没有可放的就让这个球员凑数就好了,因此不用计算贡献
}
if (sum < res * 2) return 1;
return 0;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> p[i], sum += p[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
if (!check(n)) {
cout << "-1";
return 0;
}//特判无法获胜的情况
l = 0, r = n, ans = n;
while (l <= r) {
int mid = ((l + r) >> 1);
if (check(mid)) {
r = mid - 1;
ans = mid;
} else {
l = mid + 1;
}
}
cout << ans;
return 0;
}