题意
给你一个数组 a 和数组 b, 每次操作可以任选一个 a i a_i ai, 获得收益为 a i a_i ai 的大小, 但此后的要执行 a i − = b i a_i-=b_i ai−=bi. 问执行 k 次后最大收益是多少. k 最大为 1 e 9 1e9 1e9.
思路
- 第一想法就是优先队列模拟 k 次操作, 但发现 k 很大, 铁定 tle.
- 注意到当某个 a [ i ] − x b [ i ] a[i]-xb[i] a[i]−xb[i](x 为 a[i] 被选择的次数 ) 被选择时, 所有在优先队列做法中比它大的数一定都被选过了, 假如 v a l = a [ i ] − x b [ i ] val=a[i]-xb[i] val=a[i]−xb[i] 是我最后一次操作选择的数. 那么我所有之前选择的数都不会小于 val.
- 对于一个
a
i
a_i
ai, 知道它会被选择的最小可能值, 就能算出来对他操作了多少次.
max(0, (a[i] - val) / b[i] + (a[i] >= val ? 1 : 0))
. - 我们就可以二分检查所有可能的 val, 去 check 它所对应的操作次数 times, 找到最小的 val 使 t i m e s < k times<k times<k, 可知 val 之上的所有都是被取的, 剩下的 k − t i m e s k-times k−times 次, 显然都是取 val 的.
代码:
#define int long long
int solve(int _) {
int n, k;
cin >> n >> k;
vector<int>a(n), b(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
for (int i = 0; i < n; ++i) {
cin >> b[i];
}
auto check = [&](int x) -> bool {
int num = 0;
for (int i = 0; i < n; ++i) {
num += max(0ll, (a[i] - x) / b[i] + (a[i] >= x ? 1 : 0));
}
if (num >= k)return true;
return false;
};
int l = 0, r = 1e18;
while (l < r) {
int mid = (r + l + 1) >> 1;
if (check(mid)) {
l = mid;
}
else {
r = mid - 1;
}
}
int p = r + 1, ans = 0;
for (int i = 0; i < n; ++i) {
int tim = max(0ll, (a[i] - p) / b[i] + (a[i] >= p ? 1 : 0));
k -= tim;
if (tim)ans += a[i] * tim - (tim - 1) * tim / 2 * b[i];
}
ans += k * r;
return ans;
}