https://www.luogu.org/problem/P4343
参考博客:https://www.luogu.org/blog/ofnoname/solution-p4343
这真是一语点醒梦中人啊! 其实我们只需要掌握好一种二分答案的正确写法就行了,不用非要是标准的(看不懂没关系, 说给自己听的)
以下为那位博主的分析,我觉得特别好
容易发现,对于给定的序列,n越大能过的题是越少的,所以可以二分来求刚好过k道题的左右边界。
若mid大于k,即做得太多了,就将l右移。
若mid小于k,即做得太少了,就将r左移。
求左边界,需要在mid等于k时将r左移,求右边界时则需将l右移。这个很好理解。
(这就是我们在哪写ans = mid的依据了)
印象里二分写法极多,但现在普遍应用 l<=r, l=mid+1, r=mid-1
这个版本了,虽然要多记录一个ans
,但是却在单调增单调减时都能工作,并且可以轻松应对无解的情况。
注意这种写法l应设为1,r设为无穷大即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int k, l;
long long a[100000+9];
long long check(long long n) {
long long sum = 0, num = 0;
for(int i = 1; i <= l; i++) {
sum += a[i];
sum = max(sum, (long long)0);
if(sum >= n) {num++, sum = 0;}
}
return num;
}
int main() {
scanf("%d%d",&l, &k);
for(int i = 1; i <= l; i++) scanf("%lld", &a[i]);
long long l = 1, r = 1e18, mid, mn = -1, mx = -1;// l 设为1, r为INF(真正的INF
while(l <= r) {
mid = (l+r)>>1;
if(check(mid) <= k) {
r = mid - 1;
if(check(mid) == k) mn = mid;//找最小值->需要在合法的时候把r左移,所以在更新r的地方写‘=’
} else l = mid + 1;
}
l = 1, r = 1e18;
while(l <= r) {
mid = (l+r)>>1;
if(check(mid) >= k) {
l = mid + 1;
if(check(mid) == k) mx = mid;//找最大值->需要在合法的时候把l右移, 所以... **让mx越来越大**
}else r = mid - 1;
}
if(mn == -1) printf("-1");//判断不合法情况
else printf("%lld %lld", mn, mx);
}