Codeforce 1248 E. Queue in the Train(模拟,思维)

题目大意:有 n 个人,按 [1,n] 的顺序坐在自己的位置上,每个人在 a i a_i ai的时间会醒来并且去打热水,热水一次只能一个人打,且一次需要 p p p 分钟。第 i i i 个人醒来时会先观察 [ 1 , i − 1 ] [1,i - 1] [1,i1]是否有人在排队,如果有,则第 i i i 个人会坐在自己位置上等,否则这个人会去排队。如果某一时间有多个人都醒来并且要求打热水,最左边的人会先去。

输出每个人打到热水的时间


按题意模拟即可。题意有点绕,大体需要维护三个队列,一个队列维护还在睡的人,一个维护已经醒过来但没有去排队的人(后面称准备就绪队列),一个维护已经醒来并且正在排队的人。

还在睡的人用优先级队列维护(或者 set),按时间顺序醒来,每个醒来的人要么去排队,要么扩充到准备就绪队列。

每当一个人打完水,就看看有多少人醒过来,这些醒过来的人按醒来的时间顺序判断每个人是加入到准备就绪队列还是加入到正在排队的队列。

接下来再看看准备就绪队列里有多少人可以加入到排队队列。容易发现每次最多加入一个人。循环处理即可。
详细看代码


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
struct node{
	int t,id;
	node(int ti = 0,int idi = 0) {
		t = ti;
		id = idi;
	}
	bool operator < (const node & rhs) const {
		if(t == rhs.t) return id < rhs.id;
		return t < rhs.t;
	}
};
int n,p,a[maxn];
set<node> st;
set<int> q,tq;
queue<int> pq;
ll ans[maxn];
//等待队列
//就绪队列
//原队列 
int main() {
	scanf("%d%d",&n,&p);
	for(int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
		st.insert(node(a[i],i));
	}
	ll cur = 0;
	while(st.size()) {	
		cur = st.begin() -> t;
		pq.push(st.begin() -> id);
		tq.insert(st.begin() -> id);
		st.erase(st.begin());
		while(!pq.empty()) {
			int top = pq.front();
			cur += p;
			pq.pop();
			ans[top] = cur;
			while(st.size() > 0 && st.begin() -> t <= cur) {
				if(st.begin() -> id < *tq.begin()) {
					pq.push(st.begin() -> id);
					tq.insert(st.begin() -> id);
				} else {
					q.insert(st.begin() -> id);
				}
				st.erase(st.begin());	
			}
			tq.erase(top);
			if(q.size() > 0 && (pq.empty() || *q.begin() < *tq.begin())) {
				pq.push(*q.begin());
				tq.insert(*q.begin());
				q.erase(q.begin());
			}
		}
	}
	for(int i = 1; i <= n; i++)
		printf("%lld%s",ans[i],i == n ? "\n" : " ");
	return 0;
} 
/*
1000000000
11000000009 	
*/ 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值