Educational Codeforces Round 61 (Rated for Div. 2) D. Stressful Training
D. Stressful Training
题目大意:
比赛开始每个同学都带了电脑,但没带充电器,已知有n个同学,每个同学电脑剩余电量为a[i],每秒耗电为b[i],比赛时长为k秒。教练买了个充电器,每秒钟可以给一个电脑冲x的电;求最小的x使全体同学完成比赛。ps:第一秒不耗电,最后一秒可以小于0,电量可以为0
大佬解答:
二分答案后发现可以O(nlogn)O(nlogn)判断,想办法卡卡常即可,时间复杂度O(nlognlogs)O(nlognlogs),其中s为答案的范围。
菜鸟思维:
计算x的最小值,可以很容易想到二分;
如何通过mid验证x是否成立呢?
大佬告诉我们可以在电脑电量低于0的时刻,加一,表示此刻到此刻以前电脑至少需要充电一次。维护cnt数组,cnt[i]的前缀和即表示i以前至少要充电n次,如果n>i则表示无法满足充电需求;
那么代码为
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
LL a[210000], need[210000];
int b[210000], n, k, cnt[210000];
bool check(LL mid)
{
for (int i = 0; i <= k; i++)
cnt[i] = 0;
int tot = k - 1;
for (int i = 1; i <= n; i++)
{
if (!b[i])
continue;
LL hadtime = a[i];
while (hadtime < need[i])
{
cnt[hadtime / b[i] + 1]++;
hadtime += mid;
if (!tot)
return false;
tot--;
}
}
for (int i = 1; i <= k; i++)
{
cnt[i] = cnt[i] + cnt[i - 1];
if (cnt[i] > i)
return false;
}
return true;
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
{
scanf("%d", &b[i]);
need[i] = LL(k - 1)*b[i];
}
LL left, right, mid, ans = -1;
left = 0; right = 2e12;
while (left <= right)
{
mid = (left + right) / 2;
if (check(mid))
{
ans = mid;
right = mid - 1;
}
else
left = mid + 1;
}
cout << ans << '\n';
}