本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【Codeforces】Codeforces Round594 (Div. 1) C - Queue in the Train | 模拟、STL
题目链接
题目
There are seats in the train's car and there is exactly one passenger occupying every seat. The seats are numbered from 1 to from left to right. The trip is long, so each passenger will become hungry at some moment of time and will go to take boiled water for his noodles. The person at seat i(1≤i≤n) will decide to go for boiled water at minute .
Tank with a boiled water is located to the left of the 1-st seat. In case too many passengers will go for boiled water simultaneously, they will form a queue, since there can be only one passenger using the tank at each particular moment of time. Each passenger uses the tank for exactly p minutes. We assume that the time it takes passengers to go from their seat to the tank is negligibly small.
Nobody likes to stand in a queue. So when the passenger occupying the i-th seat wants to go for a boiled water, he will first take a look on all seats from 1 to i-1. In case at least one of those seats is empty, he assumes that those people are standing in a queue right now, so he would be better seating for the time being. However, at the very first moment he observes that all seats with numbers smaller than i are busy, he will go to the tank.
There is an unspoken rule, that in case at some moment several people can go to the tank, than only the leftmost of them (that is, seating on the seat with smallest number) will go to the tank, while all others will wait for the next moment.
Your goal is to find for each passenger, when he will receive the boiled water for his noodles.
题目大意
火车车厢里有 个座位,每个座位上正好有一名乘客。座位从左到右编号为 1 到 。第 个乘客想在第 时刻去接开水。乘客的行为模式如下:
- 任意时刻只能有一名乘客使用水箱。每个乘客使用水箱恰好 分钟。我们假设乘客从座位走到水箱的时间可以忽略。
- 当第 个乘客想去接开水,他会先看一看从 1 到 i-1 的所有座位。如果这些座位中至少有一个是空的,他会暂时坐在座位上等待。在从 1 到 i-1 的所有座位全部不为空的时刻,他将会立刻去排队接水。
- 如果在某个时刻有好几个人可以去排队,那么只有最左边的人(即坐在序号最小的座位上的人)会去排队。
- 所有在排队接水的乘客按来到队伍的时间先后顺次使用水箱。
输出每个乘客成功接到水的时间,多组数据。
思路
定义事件 表示第 个人在时间 想要进入()或者离开()队伍。按时间为第一关键字、操作种类为第二关键字、人的序号为第三关键字从小到大维护优先队列,则队首元素为亟待处理的事件。
我们另开一个优先队列 用来存放所有想要进入队列但还没有进入的人,他们将会按照座位的序号顺次进入队列中去。对于时间最早的一个事件:
- 如果 ,说明第 个人想要进入队列,我们把他加入到 里
- 如果 ,说明第 个人想要离开队列,我们需要判断队列是否为空。
进行完上述两个可能的操作后,都有可能导致想要 的队首元素前面不存在为空的位置,我们要判断 的队首元素是否可以加入队列。这里我们只需要记录队列中的最小值就可以了,如果最小值大于 的队首元素,它就可以加入队列,它接完水的时间就是它加入队列的时间和前一个人接完水的时间的最大值加上他接水需要的时间 。
顺次操作直到 和事件列表均为空即可。
代码
#include <stdio.h>
#include <algorithm>
#include <queue>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
typedef pair<int,int> PII;
const int N=500001;
const LL mod=1000000007;
//const LL mod=998244353;
struct asdf{
int id,ty;
LL time;
bool operator < (const asdf a) const
{
if (time!=a.time) return time>a.time;
if (ty!=a.ty) return ty>a.ty;
return id>a.id;
}
}a;
priority_queue<asdf> pcs;
priority_queue<int,vector<int>,greater<int>> want;
int n,m,k,minn;
LL p,ans[N];
int solve()
{
scanf("%d%lld",&n,&p);
for (int i=1;i<=n;++i)
{
scanf("%lld",&a.time);
a.ty=1;
a.id=i;
pcs.push(a);
}
minn=n+1;
LL lt=0;
while (!pcs.empty())
{
asdf nw=pcs.top();
pcs.pop();
if (nw.ty==1) want.push(nw.id);
else if (nw.id==minn) minn=n+1;
if ((!pcs.empty())&&pcs.top().time==nw.time) continue;
if (!want.empty())
{
int t=want.top();
if (minn>t)
{
want.pop();
ans[t]=lt=max(lt,nw.time)+p;
pcs.push({t,0,lt});
minn=t;
}
}
}
for (int i=1;i<=n;++i) printf("%lld ",ans[i]);
}
int main()
{
int T=1;
solve();
return 0;
}