P16239 [蓝桥杯 2026 省 B] 足球训练 - 洛谷
题目描述:

解题思路:
1.如何选择第i次 训练 哪个球员?
答案:通过一个增幅函数
首先可以知道第k次球员的实力值为:
第k+1次该球员的实力值为:
我们考虑把实力值增幅看成是一个ln函数,则第k+1次和第k次差值为:
化简后:
我们称上述式子为边际收益增幅函数
只需要取对数部分,化简后:
常数1可以不考虑,只考虑,可以发现随着k变大,边际收益增幅越小, 存在一定的单调性
因此我们可以考虑二分一个边际收益r,使得所有的边际收益最后都停止在至少为r,则式子可以改写为:
继续化简为:
因此我们就推导出来了每个球员需要训练的k的次数,但是这里还有个问题,是小数,二分的时候会有精度问题。
因此我们转换到整数二分,令,若r越小则T越大,所以我们二分check一旦满足,则T往大了走。
注意一共有m次训练,但是我们二分完这个T后消耗的训练次数很可能没有达到m,此时我们需要按照增幅函数值排序,然后从大到小的拿剩下的。
代码实现
#include "bits/stdc++.h"
using namespace std;
using ll = long long;
struct node {
ll a, b, c;
bool operator< (const node& other) const {
return b * other.a > a * other.b;
}
} p[100005];
const ll mod = 998244353;
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> p[i].a >> p[i].b;
p[i].c = p[i].a / p[i].b;
}
auto check = [&](ll x) {
ll t = 0;
for (int i = 1; i <= n; i++) {
if (p[i].c > x) {
continue;
}
t += x - p[i].c;
if (t > m) {
return false;
}
}
return true;
};
ll l = 0, r = 2e9, T = 0;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid)) {
T = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
ll s = m;
for (int i = 1; i <= n; i++) {
if (T > p[i].c) {
p[i].a += (T - p[i].c) * p[i].b;
s -= (T - p[i].c);
}
}
sort(p + 1, p + 1 + n);
ll ans = 1;
for (int i = 1; i <= s; i++) {
p[i].a += p[i].b;
}
for (int i = 1; i <= n; i++) {
p[i].a %= mod;
ans = (ans * p[i].a) % mod;
}
cout << ans;
return 0;
}

3340

被折叠的 条评论
为什么被折叠?



