Codeforces 1132D - Stressful Training - [二分+贪心+优先队列]

题目链接:https://codeforces.com/contest/1132/problem/D

 

题意:

有 $n$ 个学生,他们的电脑有初始电量 $a[1 \sim n]$,他们的电脑每分钟会耗电 $b[1 \sim n]$,现在有一场比赛持续 $k$ 分钟。

要你买一个充电器,使得每个学生的电脑在比赛期间的任何时候的电量都不会低于 $0$(可以等于 $0$),你要求出这个充电器每分钟充电量最少是多少。

 

题解:

看到这种题目,应当条件反射想到二分。

假设我们现在知道充电器每分钟的充电量是 $x$,那么如何确保比赛能够进行呢?

一台电脑的初始电量为 $a$,耗电量为 $b$,如果不充电的话,显然在 $\lfloor \frac{a}{b} \rfloor+1$ 这一分钟是最后一分钟了,再下一分钟就负电量了。

所以,我们找到这个 $\lfloor \frac{a}{b} \rfloor$ 最小的电脑,这是最快用完电的那台电脑,我们应当优先给他充电。

所以在当前这一分钟,我们选择给它冲一分钟的电,很重要的一个思想,我们此时不维护每个电脑的当前电量,而是在初始电量上直接加上 $x$,因为这两个操作是等价的。

然后在下一分钟,我们继续找此时最快会没电的电脑,继续给它充一分钟的电。

这样一来,对于一个 $x$ 进行check的时间复杂度是 $O((k+n)\log n)$,而二分 $x$ 的范围是 $[0,k \cdot \max_{i=1}^{n}(b_i)]$,记 $L = k \cdot \max_{i=1}^{n}(b_i)$。所以总时间复杂度是 $O(\log L \cdot (k+n) \cdot \log n)$。

 

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;

int n,k;
ll a[maxn],b[maxn];

struct Qnode{
    ll a,b;
    ll r;
    bool operator<(const Qnode& oth)const {
        return r>oth.r;
    }
};

inline bool check(ll x)
{
    priority_queue<Qnode> Q;
    for(int i=1;i<=n;i++) Q.push((Qnode){a[i],b[i],a[i]/b[i]});
    for(int t=1;t<=k;t++)
    {
        Qnode q=Q.top(); Q.pop();
        if(q.a/q.b+1ll<t) return 0;
        if(q.a/q.b+1ll>=k) return 1;
        Q.push((Qnode){q.a+x,q.b,(q.a+x)/q.b});
    }
    return 1;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    ll bmax=0;
    for(int i=1;i<=n;i++) cin>>b[i], bmax=max(b[i],bmax);

    ll l=0, r=(k-1)*bmax+1;
    while(l<r)
    {
        ll mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    if(l>=(k-1)*bmax+1) cout<<-1<<'\n';
    else cout<<l<<'\n';
}

 

注意,这个题还有一个点,就是优先队列里的元素,我们按照 $a/b$ 来排序,需要开一个变量 $r = a / b$ 来减少六十四位除法的次数,来加快比较速度,否则会TLE。

posted @ 2019-03-11 23:08  Dilthey  阅读(613)  评论(0编辑  收藏  举报