[SHOI2015] 自动刷题机
题目背景
曾经发明了信号增幅仪的发明家 SHTSC 又公开了他的新发明:自动刷题机——一种可以自动 AC 题目的神秘装置。
题目描述
自动刷题机刷题的方式非常简单:首先会瞬间得出题目的正确做法,然后开始写程序。每秒,自动刷题机的代码生成模块会有两种可能的结果:
1.写了
x
x
x 行代码
2.心情不好,删掉了之前写的
y
y
y 行代码。(如果
y
y
y 大于当前代码长度则相当于全部删除。)
对于一个 OJ,存在某个固定的正整数长度 n n n,一旦自动刷题机在某秒结束时积累了大于等于 n n n 行的代码,它就会自动提交并 AC 此题,然后新建一个文件(即弃置之前的所有代码)并开始写下一题。SHTSC 在某个 OJ 上跑了一天的自动刷题机,得到了很多条关于写代码的日志信息。他突然发现自己没有记录这个 OJ 的 n n n 究竟是多少。所幸他通过自己在 OJ 上的 Rank 知道了自动刷题机一共切了 k k k 道题,希望你计算 n n n 可能的最小值和最大值。
输入格式
第一行两个整数 l , k l , k l,k,表示刷题机的日志一共有 l l l 行,一共了切了 k k k 题。
接下来 l l l 行,每行一个整数 x i x_i xi,依次表示每条日志。若 x i ≥ 0 x_i \geq 0 xi≥0,则表示写了 x i x_i xi 行代码,若 x i < 0 x_i \lt 0 xi<0,则表示删除了 − x i -x_i −xi 行代码。
输出格式
输出一行两个整数,分别表示
n
n
n 可能的最小值和最大值。
如果这样的
n
n
n 不存在,请输出一行一个整数
−
1
-1
−1。
样例 #1
样例输入 #1
4 2
2
5
-3
9
样例输出 #1
3 7
提示
数据规模与约定
- 对于 20 % 20\% 20% 的数据,保证 l ≤ 10 l \le 10 l≤10;
- 对于 40 % 40\% 40% 的数据,保证 l ≤ 100 l \le 100 l≤100 ;
- 对于 60 % 60\% 60% 的数据,保证 l ≤ 2 × 1 0 3 l \le 2 \times 10^3 l≤2×103;
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ l ≤ 1 0 5 1 \leq l \le 10^5 1≤l≤105, − 1 0 9 ≤ x i ≤ 1 0 9 -10^9 \le x_i \le 10^9 −109≤xi≤109。
思路
本道题题目有要求最大值和最小值,又看数据范围, 1 ≤ l ≤ 1 0 5 1 \leq l \le 10^5 1≤l≤105,因此我们可以用二分法来求答案,(因为本道题和切木头的那道题很像)。
- 细节1:这道题最需要注意的是:当 x i ≤ 0 x_i\le0 xi≤0的时候,那么我们要把它变成0,因为这句话:如果 y y y 大于当前代码长度则相当于全部删除。
- 细节2:因为我们要求它们的和,可能会爆int,因此得开long long。
- 细节3:题目要求边界问题,因此我们看我们求出的答案是否满足刚刚好等于 m m m,如果是的话,那就满足条件。
- 细节4:求最大值和最小值用二分实现,我们得及时调整
check
函数的return
条件。
代码
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N = 2e5+10,M = 1e18;
int w[N],n,m;
bool check1(int x){
int s=0;
int cnt=0;
for(int i=1;i<=n;i++){
s+=w[i];
if(s>=x)s=0,cnt++;
else if(s<0)s=0;
}
return cnt<=m;
}
bool check2(int x){
int s=0;
int cnt=0;
for(int i=1;i<=n;i++){
s+=w[i];
if(s>=x)s=0,cnt++;
else if(s<0)s=0;
}
return cnt>=m;
}
bool istrue(int x){
int s=0;
int cnt=0;
for(int i=1;i<=n;i++){
s+=w[i];
if(s>=x)s=0,cnt++;
else if(s<0)s=0;
}
return cnt==m;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i];
}
int l=0,r=M;
while(l+1!=r){
int mid=l+r>>1;
if(check1(mid))r=mid;
else l=mid;
}
int res=istrue(r)?r:-1;
l=0,r=M;
while(l+1!=r){
int mid=l+r>>1;
if(check2(mid))l=mid;
else r=mid;
}
int ans=(istrue(l)?l:-1);
if(ans==-1&&res==-1){
cout<<-1;
}else{
cout<<res<<" "<<ans;
}
return 0;
}