题目
题目概要
n
n
n 台电脑,每台电脑有电量
a
i
a_i
ai 和耗电量
b
i
b_i
bi ,在每秒钟的末尾扣除电量。有一充电器,每次能够充
c
c
c 的电量,每秒钟的开始可以给一台电脑充电。第
t
t
t 秒的末尾早于第
t
+
1
t+1
t+1 秒的开始。
现在是 0 0 0 时刻,也就是第一秒的开始。在第 k k k 秒的末尾,所有电脑都会爆炸,但在此之前,不要让电量变为负数。电量为零是被允许的。第 k k k 秒的末尾时,电脑电量可以为负。
求出满足条件的最小 c c c 。 c c c 不存在,输出 − 1 -1 −1 。
数据范围与提示
1
≤
n
,
k
≤
2
×
1
0
5
1\le n,k\le 2\times 10^5
1≤n,k≤2×105 ,
1
≤
a
i
≤
1
0
12
1\le a_i\le 10^{12}
1≤ai≤1012 ,
1
≤
b
i
≤
1
0
7
1\le b_i\le 10^7
1≤bi≤107 。
思路
一眼二分答案。现在我们该如何检查?
考虑电量为 a a a 、耗电量为 b b b 时,能够撑多久。显然,如果过了 t t t 天就撑不下去了,一定满足 a − b t < 0 a-bt<0 a−bt<0 即 ⌊ a b ⌋ < t \lfloor\frac{a}{b}\rfloor<t ⌊ba⌋<t 。
那么,对于一个电脑,他本来有 a a a 的电量,耗电量为 b b b ,充电器效率为 c c c ,则 x x x 次充电后能撑到 ⌊ a + x c b ⌋ + 1 \lfloor\frac{a+xc}{b}\rfloor+1 ⌊ba+xc⌋+1 时刻,所以在这一秒的开始必须给此电脑充电。
因为每一个电脑都要撑到 k k k 时刻,可以将每个最晚充电时间都求出,并塞入桶中。最后检查,如果前 r r r 秒需要充电至少 r + 1 r+1 r+1 次,一定不行。
是否存在 c c c 如何判断?显然最好的情况就是,一次充电就够用,即 c ≥ b i k c\ge b_ik c≥bik ,将这个值代入检查一次即可。或者用其作为二分边界。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
inline long long readint(){
long long a; scanf("%lld",&a); return a;
}
const int MaxN = 200005;
long long a[MaxN], b[MaxN];
int cnt[MaxN], n, k;
bool check(long long v){
for(int i=0; i<k; ++i)
cnt[i] = 0;
int tot = 0;
for(int i=1; i<=n; ++i){
long long ne = a[i]/b[i]+1;
long long t = a[i];
while(ne < k && tot < k){
++ tot, ++ cnt[ne];
ne = (t += v)/b[i]+1;
}
if(tot == k) return false;
}
if(cnt[0]) return false;
for(int i=1; i<k; ++i){
cnt[i] += cnt[i-1];
if(cnt[i] > i)
return false;
}
return true;
}
int main(){
n = readint(), k = readint();
for(int i=1; i<=n; ++i)
a[i] = readint();
for(int i=1; i<=n; ++i)
b[i] = readint();
long long L = 0, R = 2000000;
R *= R; // R >= 2e16 即可
while(L != R)
if(check((L+R)>>1))
R = (L+R)>>1;
else L = (L+R)/2+1;
if(!check(L)) puts("-1");
else printf("%lld\n",L);
return 0;
}