D.Stressful Training
题意
有n台电脑,第i台初始电量为 a i a_i ai,每分钟消耗电量为 b i b_i bi,现在要购买一个充电器,每分钟可以让一个电脑的电量增加 X X X,比赛一共持续k分钟,充电器在一分钟之内只能给一个电脑充电,问能让所有学生成功完成比赛的最小的 X X X(比赛中途不能出现电量小于 0 0 0,如果恰好在最后一分钟电量为负,也算成功完成)。
做法
首先这个我们把这个 k k k减 1 1 1,因为最后一分钟是无效的,我们不需要考虑,之后我们知道这个答案肯定是可以二分的,所以先二分答案,之后对于每个当前选择的功率,去算每个电脑要在哪些时刻必须充电,在这些时刻标记上,如果被标记的次数和超过 k k k,表示最后一分钟之前需要充电的电脑数已经超过 k k k,直接返回false,如果总次数不超过k,我们就用sum[i]表示到i为止需要充电的电脑数,只要对于每个i都有i>=sum[i]就可以完成比赛。注意如果在最后一分钟充电不需要算入次数内。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
ll a[maxn],b[maxn],c[maxn];
int n,k;
int sum[maxn];
bool check(ll mid)
{
memset(sum,0,sizeof(sum));
int tot=k;
for(int i=1;i<=n;i++)
{
ll tmp=a[i];
if(tmp>=1LL*k*b[i]) continue;
while(tmp<1LL*k*b[i])
{
if(tot==0)
{
if(min(tmp/b[i],(ll)k)+1==k+1) break;
else return false;
}
sum[min(tmp/b[i],(ll)k)+1]++;
tmp+=mid;
tot--;
}
}
ll cc=0;
for(int i=1;i<=k;i++)
{
cc=cc+sum[i];
if(cc>i) return false;
}
return true;
}
int main()
{
scanf("%d%d",&n,&k);
k--;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
ll l=0,r=1000000000000000000,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
if(l==1000000000000000001) puts("-1");
else printf("%lld\n",l);
return 0;
}