CF1945G Cook and Porridge

tags

模拟 优先队列

中文题面

n n n 个人排队喝粥,对于第 i i i 个人,他有一个优先程度 k i k_i ki,并且喝一碗粥需要花费 s i s_i si 分钟。

供应粥的厨师总共工作 D D D 分钟,对于每一分钟,他会给排在队伍最前面的一个人打一碗粥,随后这个人离开队伍去喝粥。假设这个人是第 i i i 个人,并且此时是第 x x x 分钟,那么在第 x + s i x+s_i x+si 分钟这个人归队,队伍最后面所有优先程度严格小于他的人站在他后面,换句话说,这个人最终站的位置的后面所有的人的优先程度都小于他,这个人紧挨着的前面的一个人的优先程度大于等于他。

试问在 D D D 分钟内每个人能否都喝到粥?若可以,报告是在第几分钟;若不可以,输出 -1

输入格式

每个测试由几个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 1000 1 \le t \le 1000 1t1000 )——测试用例的数量。接下来是测试用例的描述。

每个测试用例的第一行包含两个整数 n n n D D D ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105 1 ≤ D ≤ 3 ⋅ 1 0 5 1 \le D \le 3\cdot 10^5 1D3105 ) -分别表示队列中的学童人数和休息时间。

接下来的 n n n 行包含两个整数 k i k_i ki s i s_i si ( 1 ≤ k i , s i , ≤ 1 0 9 1 \le k_i, s_i, \le 10^9 1ki,si109 ) -分别表示小学生吃一份粥的优先级和时间。学生们按排队的顺序排列(从前面到最后)。

保证所有输入数据集的 n n n 值的总和不超过 2 ⋅ 1 0 5 2\cdot 10^5 2105。同样,可以保证所有输入数据集的 D D D 值的总和不超过 3 ⋅ 1 0 5 3\cdot 10^5 3105

输出格式

对于每个测试用例,输出每个学童至少吃一次粥的最小分钟数。如果在中断时间内没有发生,则输出 − 1 -1 1

样例输入

7
3 3
2 2
3 1
2 3
5 10
10 3
7 1
11 3
5 1
6 1
5 20
4 2
7 2
8 5
1 5
3 1
5 17
1 3
8 2
8 3
2 2
1 1
5 14
8 2
4 2
1 3
8 3
6 4
1 11
4 5
5 14
8 2
4 2
1 3
8 3
6 4

样例输出

3
-1
12
6
6
1
6

思路

本题思路比较直接,由于题面规定 D D D 数据最大为 3 × 1 0 5 3\times 10^5 3×105,且多测样例 ∑ D ∑ D D 不超过 3 × 1 0 5 3\times 10^5 3×105,我们可以考虑模拟思想,一分钟一分钟的走完这个过程。

从时间复杂度上来说,如果每分钟所做操作的时间复杂度不超过 O ( l o g 2 n ) O(log_2n) O(log2n),则可以得到一个总复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) 的算法,时间复杂度上可行。

我们现在来看如何进行这样的模拟。

我们不妨在当前时间 n o w now now 增加的同时遍历原数组,对于每个即将归队的学生记录在册,如果遍历到原数组中第 i i i 人时:

  1. 假设此时是第 d d d 分钟,没有归队在他前面的人,那么显然这个人在第 d d d 分钟分到了粥
  2. 否则,把粥分给归队在第 i i i 人前面的人,继续下一分钟

本题得解,只需要注意归队顺序即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N (int)2e5 + 5
#define pii pair<int, int>

int d, n, t;
pii sk[N];
int dp[N];
map<int, int> mp;
struct node {
    int t, k, s;
    bool operator < (const node & b) const {
        if (this->k!=b.k) return this->k < b.k;
        if (this->t!=b.t) return this->t > b.t;
        return this->s > b.s;
    }
};
struct mid {
    int t, k, s;
    bool operator < (const mid& b) const {
        return this->k < b.k;
    }
};
struct Node {
    int t, k, s;
    bool operator < (const Node& b) const {
        return this->t > b.t;
    }
};
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> t;
    while (t--) {
        priority_queue<node> q;
        priority_queue<Node> Q;
        priority_queue<mid> M;
        cin >> n >> d;
        for (int i = 1; i <= n; i++) dp[i] = 0;
        for (int i = 1; i <= n; i++) {
            cin >> sk[i].first >> sk[i].second;
        }
        int now = 1;
        vector<int> mx(n + 1);
        for (int i = 1; i <= n; i++) mx[i]=sk[i].first;
        for (int i = n - 1; i >= 1; i--) {
            mx[i] = max(mx[i], mx[i + 1]);
        }
        for (int i = 1; i <= n && now <= d; i++) {
            while (Q.size() && Q.top().t <= now) {
                M.push({Q.top().t, Q.top().k, Q.top().s});
                Q.pop();
            }
            while (M.size() && M.top().k > mx[i]) {
                q.push({M.top().t, M.top().k, M.top().s});
                M.pop();
            }
            while (q.size() && now <= d) {
                Q.push({now + q.top().s + 1, q.top().k, q.top().s});
                q.pop();
                now++;
                while (Q.size() && Q.top().t <= now) {
                    M.push({Q.top().t, Q.top().k, Q.top().s});
                    Q.pop();
                }
                while (M.size() && M.top().k > mx[i]) {
                    q.push({M.top().t, M.top().k, M.top().s});
                    M.pop();
                }
            }
            Q.push({now + sk[i].second + 1, sk[i].first, sk[i].second});
            dp[i] = now++;
        }
        cout << (dp[n]!=0&&dp[n]<=d?dp[n]:-1) << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值