简化题面:一个数,初始为0。(l)次操作,每次给这个数加上 (xi)((xi)可能为负),如果其小于0则变成0,如果大于等于(n)则变成0并给(ans)加1。已知最后的(ans)等于(k),求(n)可能的最小值和最大值。如果没有满足条件的(n),输出-1。
对于一眼看到此题,很难想到二分来做,考虑怎么想到的二分
举个栗子
假如两个(n),一个8一个9,假如第一次增加9,对于(n=8)来说剩余1,对于(n=9)来说剩余0,在剩余的增加次数中无论怎样8总会领先于9(ans)到达(k),因此可见对于不同的(n),对于到达(k)的(ans)的贡献是单调的,可以进行二分
想到了二分,此题就很好做了,我们二分(n),对于答案的判断直接进行题面模拟的,逐步压缩空间直到符合答案
有关二分
对于二分有两种格式,下面简单提一下
对于求符合答案区间的最小值,也就是左边箭头所指的值,我们的二分过程如下
while(l < r){
int mid = (l + r) >> 1;
if(a[mid] >= x) r = mid;else l = mid + 1;
}
return a[l];
对于最大值,也就是右边箭头所指的值,我们的二分过程如下
while(l < r){
int mid = (l + r + 1) >> 1;
if(a[mid] <= x) l = mid; else r = mid - 1;
}
return a[l];
个人认为二分类型的题很练人的细节程度,建议仔细品一品过程并且自己模拟一下~
(Code)
#include<bits/stdc++.h>
#define LL long long
#define int long long
#define _ 0
using namespace std;
/*Grievous Lady*/
template <typename T> void read(T & t){
t = 0;int f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f =- 1;ch = getchar();}
do{t = t * 10 + ch - '0';ch = getchar();}while(ch >= '0' && ch <= '9');t *= f;
}
#define INF 0x3f3f3f3f3f3f3f3fLL
const int kato = 1e5 + 10;
int a , k;
int x[kato];
inline int judge(int mid){
int sum = 0 , tot = 0;
for(int i = 1;i <= a;i ++){
sum += x[i];
if(sum < 0) sum = 0;
if(sum >= mid) tot ++ , sum = 0;
}
return tot;
}
inline int Ame_(){
read(a);read(k);
for(int i = 1;i <= a;i ++){
read(x[i]);
}
int la = 1 , ra = INF , ansa = INF;
while(la < ra){
int mida = (la + ra) >> 1;
if(judge(mida) <= k){
ra = mida;
}
else la = mida + 1;
// cout << ansa << '
';
}
if(judge(la) == k) ansa = la;
if(ansa != INF) printf("%lld " , ansa);
else printf("-1 ");
int lb = 1 , rb = INF , ansb = INF;
while(lb < rb){
int midb = (lb + rb + 1) >> 1;
if(judge(midb) >= k){
lb = midb;
// if(judge(lb) == k) ansb = lb;
}
else rb = midb - 1;
}
if(judge(lb) == k) ansb = lb;
if(ansb != INF) printf("%lld
" , ansb);
return ~~(0^_^0);
}
int Ame__ = Ame_();
signed main(){;}