题目就不再赘述了。代码注释已经解释得很清楚了,链表不是很方便么,为什么没多少人用呢?
而且顺序扫描时链表是比set少一个log的
至于时间复杂度……实测96ms
/*不错的题目
题目的意思就是在某个时刻给你一段长度已知的区间,让你塞入一个大区间里面,然后加了一些
花里胡哨的东西。其实就是模拟,但问题是如何维护。对于一个进程,它之前的一些进程已经占用了
一部分空间,也就是说,当前进程需要在夹缝中找地方放。
比如说,1表示占用,0表示没占用
1111 000000 111 00 11 0000
方便起见,在首尾加一个1,限制范围
1 1111 000000 111 00 11 0000 1
现在当前的进程就要在夹缝中生存。一个简单的想法,维护被覆盖的地方。当然不可能开hash,看数据
就知道,所以我们可以用链表维护,又简单又好写,没必要用set。然后一个一个扫过去,相邻两块的
左右端点作差,看看放不放的下。当然,在放内存之前要删除结束的内存,并先放队列里的等待内存,
具体见代码注释
*/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 100005;
struct Info{
int T, M, P;
};
struct Node{
int l, r, pre, nxt;
}node[N];
int maxn, n, tot, he, ta, ans, ti;
queue<Info> qq;
priority_queue<pii, vector<pii>, greater<pii> > pq; //用小根堆维护结束最早的进程
//链表版子,可参照蓝书
inline void init(){ //初始化,he、ta为左右边界,方便计算
tot = 2;
he = 1, ta = 2;
node[he].nxt = ta;
node[he].r = 0;
node[ta].pre = he;
node[ta].l = maxn + 1;
}
inline void remove(int p){ //删除节点
node[node[p].pre].nxt = node[p].nxt;
node[node[p].nxt].pre = node[p].pre;
}
inline void insert(int p, int l, int r){ //插入节点
int q = ++tot;
node[q].l = l, node[q].r = r;
node[node[p].nxt].pre = q;
node[q].nxt = node[p].nxt;
node[p].nxt = q;
node[q].pre = p;
}
inline int find(int M){ //找夹缝
for (int i = he; i != ta; i = node[i].nxt)
if (node[node[i].nxt].l - node[i].r > M) return i; //返回节点编号会比较方便
//可以自己推一下,式子为什么是这样
return -1;
}
inline int solve(Info tmp, int now_T){ //注意一下是now_T,因为会有队列里的进程,初始时间并不一定是放入时间
int id = find(tmp.M);
if (id == -1){ //没找到
return 0;
}else{ //找到了,插入节点
insert(id, node[id].r + 1, node[id].r + tmp.M);
pq.push(make_pair(now_T + tmp.P, tot));
return 1;
}
}
int main(){
scanf("%d", &maxn);
init();
while (1){
int T, M, P;
scanf("%d%d%d", &T, &M, &P);
if (T + M + P == 0) break;
while (pq.size() && pq.top().first <= T){ //说明有进程结束,现在可以删除了
pii tmp = pq.top(); pq.pop();
remove(tmp.second);
if (pq.size() && tmp.first == pq.top().first) continue;
//注意这里,结束时间相等算同一时刻,一定要一起处理,不然的话,可能后续的进程放不进去,其实是自己没删完
while (qq.size()){ //题目中说的,一有机会就放
if (solve(qq.front(), tmp.first)) qq.pop(); //说明在处理了,从队列里删除
else break;
}
}
if (!solve((Info){T, M, P}, T)) //说明现在放不进去,入队
++ans, qq.push((Info){T, M, P});
}
while (pq.size()){ //可以保证这里一定是会运行的,上面结束后堆一定不是空的,最长时间可以在这里算
pii tmp = pq.top(); pq.pop();
ti = max(ti, tmp.first); //除了算时间,下面都是一样的了
remove(tmp.second);
if (pq.size() && tmp.first == pq.top().first) continue;
while (qq.size()){
if (solve(qq.front(), tmp.first)) qq.pop();
else break;
}
}
printf("%d\n%d\n", ti, ans);
return 0;
}
去掉了注释后,代码其实没多长……
顺序扫描平衡树是均摊O(1)的
建议手写红黑树测试复杂度其实set顺序扫描也是$O(n)$的,只是STL常数大而已