按计算机分别维护当前运行任务的小根堆,先弹出已结束任务,再判断剩余算力是否足够。
OJ: luogu
题目 ID: P8755
难度:普及/提高-
标签:模拟堆优先队列
日期: 2026-06-21 12:36
题意
有 n 台计算机,每台有固定算力。
每个任务会在时刻 a 被分配到指定机器 b,持续 c 秒,占用算力 d。
如果分配时该机器剩余算力不足,就输出 -1;否则任务成功开始运行,输出分配后的剩余算力。
思路
先看一个可以直接验证想法的朴素解:
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Task {
ll end_time;
ll cost;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> cap(n + 1);
for (int i = 1; i <= n; i++) {
cin >> cap[i];
}
vector<vector<Task> > tasks(n + 1);
vector<ll> used(n + 1, 0);
for (int i = 1; i <= m; i++) {
ll a, b, c, d;
cin >> a >> b >> c >> d;
vector<Task> alive;
used[b] = 0;
for (Task t : tasks[b]) {
if (t.end_time > a) {
alive.push_back(t);
used[b] += t.cost;
}
}
tasks[b].swap(alive);
if (cap[b] - used[b] < d) {
cout << -1 << '\n';
continue;
}
tasks[b].push_back({a + c, d});
used[b] += d;
cout << cap[b] - used[b] << '\n';
}
return 0;
}最直接的想法是:对每台机器维护当前正在运行的任务列表。
每来一个新任务,就先把这台机器上所有已经结束的任务删掉,再看剩余算力是否够。
真正的问题只在于:
- 怎样快速找到“已经结束的任务”
因为每个任务一旦开始,它的结束时刻 a + c 就确定了,所以对每台机器维护一个按结束时刻排序的小根堆即可。
处理一个新任务 (a, b, c, d) 时:
- 不断弹出
b号机器里所有end_time <= a的任务 - 同时把这些任务占用的算力从
used[b]里减掉 - 判断
cap[b] - used[b]是否至少为d - 如果够,就把新任务压入堆里
因为每个任务只会入堆一次、出堆一次,所以总复杂度是可控的。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 200005;
struct Node {
ll end_time;
ll cost;
bool operator < (const Node &other) const {
return end_time > other.end_time;
}
};
int n, m;
ll cap[MAXN];
ll used[MAXN];
priority_queue<Node> pq[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> cap[i];
}
for (int i = 1; i <= m; i++) {
ll a, b, c, d;
cin >> a >> b >> c >> d;
// 先释放掉这台机器上所有已经结束的任务。
while (!pq[b].empty() && pq[b].top().end_time <= a) {
used[b] -= pq[b].top().cost;
pq[b].pop();
}
if (cap[b] - used[b] < d) {
cout << -1 << '\n';
continue;
}
used[b] += d;
pq[b].push({a + c, d});
cout << cap[b] - used[b] << '\n';
}
return 0;
}复杂度
每个任务最多入堆一次、出堆一次,所以总复杂度是
更准确地说,是所有机器堆操作次数总共
空间复杂度是
总结
这题本质上是“按机器分组的事件模拟”。
关键不是复杂数据结构,而是抓住:
- 结束任务要按最早结束的先释放
于是每台机器用一个小根堆就足够了。