解题报告
注意到一个性质(当然不注意到一般也能想到): n 越大,能过的题就越少。这是一个单调的性质。
考虑二分这个 n,最大值和最小值分开做都一样的。这里讲最小值。
check
函数很好想,传进去当前二分的 n,然后按题意模拟,获得实际 AC 数,和给定的 AC 数对比一下确定是高了还是低了,然后更新边界即可。
最大值最小值更新边界的区别可以手玩验证。原则是:求最小值就尽量调低,求最大值就尽量调高。
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <set>
#include <map>
// 性质:N 越大,能过的题越少
const int MAXL = 1e5 + 10;
int l; long long int k;
long long int mnans = -1, mxans;
long long int logg[MAXL];
long long int check(long long int fn) {
long long int exist = 0, acc = 0;
for (int i = 1; i <= l; ++i) {
exist = std::max(exist + logg[i], 0ll);
if (exist >= fn) { ++acc; exist = 0; }
}
return acc; // 过题数
}
int main() {
scanf("%d %lld", &l, &k);
for (int i = 1; i <= l; ++i) scanf("%lld", logg + i);
long long int l = 1, r = 0x7f7f7f7f7f7f7f7f;
while (l <= r) { // min
long long int mid = (l + r) >> 1ll;
long long int acc = check(mid);
if (acc <= k) {
r = mid - 1;
if (acc == k) mnans = mid; // 满足条件更新答案
} else l = mid + 1;
} if (mnans == -1) {
puts("-1"); return 0;
}
l = 1, r = 0x7f7f7f7f7f7f7f7f;
while (l <= r) { // max
long long int mid = (l + r) >> 1ll;
long long int acc = check(mid);
if (acc >= k) {
l = mid + 1;
if (acc == k) mxans = mid;
} else r = mid - 1;
} printf("%lld %lld", mnans, mxans);
return 0;
}