CF1132D Stressful Training 题解
link
「不开 long long 见祖宗!!!」
0x00 思路
先看题。
「有 \(n\) 个学生要打一场 \(k\) 分钟的比赛(当然要用电脑)。」
「每个学生的电脑有初始电量 \(a_i\) 和每分钟耗电量 \(b_i\)(电量在这一分钟的最后一刻结算,即在下一分钟时才会减少,电量允许为负)。」
「这样肯定是无法打完比赛的,所以学生们买了一个充电器,功率为任意值,每分钟可以使电量增加 \(x\),结算规则与耗电量一样,它可以在任一分钟给任一学生的电脑充电任意时长。」
「问题:求最小的 \(x\),使所有学生的电脑的电量在 \(k\) 分钟内都不为负。」
由于是求最小,所以如果这个功率的充电器是可行的,那么所有功率比它大的都一定可行。
所以这道题目答案有单调性,于是我们可以选择二分答案。
注意:虽然题目说了电量可以为负,但是这个时候就打不了比赛了啊!
由于常识,只要电量大于等于 \(0\),就可以继续用。什么鬼常识所以我们只需要找到充电器使得所有人电脑在任意时刻电量大于等于 \(0\) 即可。
0x01 Check
由于我们先前已经二分了一个我们觉得有可能的答案(假设为 \(c\)),我们肯定需要去打一个模拟赛去模拟正式比赛时的真实情况。
那么这个时候我们就遇到了一个问题:每个人的电脑似乎都需要充电,到底充谁的好呢?
由于题目的标签是贪心 + 二分,因为我们要保证每个人的电脑都能撑到最后一刻,那么目前看来最危险的电脑就是 当前电量能支撑的时间(不是电量!!!!) 最少的那一台。所以为了帮助它渡过难关,当前分钟肯定会给其充电啦~
所以我们的贪心策略就出现了:找到当前能支撑的时间最少的一台电脑(这时可以用优先队列直接取队首)并在当前时间给其充电。
接着我们就可以写出代码了。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct Mc
{
long long now,elec;//电量,耗电率
double times;//支撑时间
}a[200005],s,h;
bool operator<(Mc a,Mc b)
{
return a.times>b.times;
}
int n,k;
bool check(long long c)
{
priority_queue<Mc>q;
int i;
for(i=1;i<=n;i++)//入队
q.push(a[i]);
for(i=1;i<k;i++)
{
s=q.top();
q.pop();
if(s.times-i<-1)//这台电脑已经没电了
return 0;
s.times+=c*1.0/s.elec;//充电
q.push(s);
}
return q.top().times-k>=-1;
}
long long ef(long long l,long long r)//二分模板,没什么好说的
{
int i;
if(l==r)
{
if(check(l))
return l;
return -1;
}
long long mid=(l+r)/2;
if(check(mid))
return ef(l,mid);
return ef(mid+1,r);
}
signed main()
{
int i,j;
scanf("%lld %lld",&n,&k);
for(i=1;i<=n;i++)
scanf("%lld",&a[i].now);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i].elec);
a[i].times=a[i].now*1.0/a[i].elec;//计算在最开始每台电脑能支撑的时间
}
printf("%lld",ef(0,2e12/*充电器最大功率*/));
return 0;
}
对代码的解释:
s=q.top();
q.pop();
if(s.times-i<-1)//这台电脑已经没电了
return 0;
Q:为什么用队首来判断还能否坚持?
A:因为队首的电脑能支撑的时间最短,如果其可以支撑则其他的都可以,否则就说明已经没不行了。

浙公网安备 33010602011771号