题目
题目背景
曾经发明了信号增幅仪的发明家 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
最大可能行数为
1
0
5
×
1
0
9
=
1
0
14
10^5 \times10^9 = 10^{14}
105×109=1014
枚举效率低
所以想到二分答案
具体实现
先求出当前题数所需行数的最大值
再求出当前题数+1所需行数的最大值,然后对它+1,就是当前题数所需行数的最小值
若最小值大于最大值则输出-1
否则输出最小值与最大值
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<bitset>
using namespace std;
inline long long read() {
long long x = 0, f = 1; char s = getchar();
while (s < '0' || s>'9') { if (s == '-')f = -1; s = getchar(); }
while (s >= '0' && s <= '9') x = x * 10 + s - '0', s = getchar();
return x * f;
}
inline void write(long long x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
long long arr[100011];
using namespace std;
int main() {
long long m, n;
m = read(); n = read();
for (int i = 0; i < m; i++)arr[i] = read();
long long l = 1, r = 100000000000000000;
long long ans = 100000000000000000;
while (l <= r) {
long long mid = (l + r) / 2;
//long long cnt=0;
long long ak_num = 0;
long long lines = 0;
for (int i = 0; i < m; i++) {
lines += arr[i];
if (lines < 0)lines = 0;
if (lines >= mid)lines = 0, ak_num++;
}
if (ak_num >= n) {
ans = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
long long ans1 = 100000000000000000;
l = 1, r = 100000000000000000;
while (l <= r) {
long long mid = (l + r) / 2;
long long ak_num = 0;
long long lines = 0;
for (int i = 0; i < m; i++) {
lines += arr[i];
if (lines < 0)lines = 0;
if (lines >= mid)lines = 0, ak_num++;
}
if (ak_num > n) {
ans1 = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
long long ak_num = 0;
ans1++;
long long lines=0;
if (ans1 > ans)write(-1);
else cout << ans1 << ' ' << ans;
}
然而交上去只有95分
小细节
hack数据:
4 4
114
514
1919
810
正确输出是 1 114
而该代码输出 -1
因为当题数为5时,不可能成功
因而要进行特判
容易发现这种情况下下限为1
特判代码如下:
for (int i = 0; i < m; i++) {
lines += arr[i];
if(lines)
if (lines >= 1) {
lines = 0; ak_num++;
}
}
if (ak_num == n)ans1 = 1;