Codeforces 725D. Contest Balloons

tags

模拟 贪心 优先队列(堆)

中文题面

ACM-ICPC 竞赛的一个传统是,每解决一个问题,参赛队就能得到一个气球。我们假设提交时间并不重要,参赛队只按气球数量排序。这意味着一个人的名次等于拥有更多气球的队伍数,再加上 1 。例如,如果有七支队伍的气球数量多于你,你将获得第八名。允许出现平局。

要知道,比赛前吃东西很重要。如果一队的气球数大于该队的重量,该队就会开始和他们的工作台一起漂浮在空中。他们最终会触及天花板,而这是规则严格禁止的。该队将被取消比赛资格,不计入积分榜。

比赛刚刚结束。有 n 支队伍,编号为 1 到 n 。第 i 支队伍有 ti 个气球,重量为 wi 。保证 ti 不超过 wi ,所以一开始没有人浮起来。

Limak 是第一队的成员。他不喜欢作弊,也不会偷其他队的气球。相反,他可以把自己的气球送给其他队,可能会让他们浮起来。Limak 可以赠送本队的零个或更多气球。显然,他赠送的气球数量不能超过自己队伍最初拥有的气球数量。

Limak 所能得到的最好名次是多少?
输入
标准输入的第一行包含一个整数 n ( 2 ≤ n ≤ 300 000 ) - 队数。

接下来 n 行中的 i 行包含两个整数 ti 和 wi 。( 0 ≤ ti ≤ wi ≤ 1018 ) - 分别是第 i 个小组的气球数量和重量。利马克是第一队的队员。
输出
打印一个整数,表示 Limak 能获得的最佳位置。

思路

不妨思考每一步,放飞名次排在自己后面的人毫无意义,因为这样自己的名次不但不会得到上升,还有可能因失去气球而下降。这样我们得到了每一步的最优做法:尝试放飞名次比自己靠前的,同时我们想让自己气球数下降的尽可能少以保持名次,则优先放飞所需放飞气球数少的队(使用优先队列使其在堆中上浮)。每次放飞后我们立刻查询自己当前的名次,更新最好名次,单次查询O(n)则必然超时,不妨排序后使用二分查找单次查询O(logn),更新最好名次后再更新优先队列,单次更新O(logn),最多更新n次,总时间复杂度O(nlogn),可以AC

代码

// Edit by Mr_Way
// 动态性的 对于名次本来就在自己后面的先不用考虑,先干掉前面的,优先干掉需气球少的
// O(nlogn)似乎可以实现
#include <bits/stdc++.h>
using namespace std;
#define int long long

int n, now, tmp, ans, x, d, best, last, ran, cnt = 0;
struct node {
    int val;
    int d;
} t[300005];
inline bool cmp(const node& a, const node& b) {
    return a.val > b.val;
}
inline int Rank() {
    int l = 1, r = n - 1;
    ans = n;
    while (l <= r) {
        int mid = l + r >> 1;
        if (t[mid].val > now) l = mid + 1;
        else ans = mid, r = mid - 1;
    }
    return ans;
}

signed main() {
    priority_queue<int, vector<int>, greater<int>> q;
    cin >> n;
    cin >> now >> tmp;
    for (int i = 1; i <= n - 1; i++) {
        cin >> t[i].val >> t[i].d;
        t[i].d -= t[i].val - 1;
    }
    sort(t + 1, t + n, cmp);
    best = Rank();
    last = best;
    for (register int i = 1; i < best; i++) q.push(t[i].d);
    while (q.size()) {
        if (now < q.top()) break;
        now -= q.top();
        q.pop();
        cnt++;
        ran = Rank();
        for (register int i = last; i < ran; i++) q.push(t[i].d);
        last = ran;
        best = min(best, ran - cnt);
    }
    cout << best;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值