题解:CF1239C Queue in the Train
前言
奇怪的 STL 应用题,但说实话比较考验思维。
思路
显然,对于每一个时刻,都有这三类人:
对于第一个,我们用一个小根堆,来存他们想加入队列的时间,因为显然优先考虑想先去接水的人。同时要存储人的编号,因为你后面总会把这些人插到
对于第二个,同样用一个小根堆,这个是存编号。因为大家都想要去接水了,就要优先考虑编号小的。
对于第三个用一个普通队列即可。因为顺序已经定下来了,不可能去插队吧。而且题目中说了正在饮水机旁排队的所有人形成一个先进先出的队列。
这里就一个性质:等待队列里的数单调递减。
很简单,因为题目中说了只有比自己编号小的都没去自己才有资格去。
这个性质比较重要。
以下设
最开始把所有元素都加入到
考虑等待队列空了,有两种情况:
- 如果
want 没空,让want 中编号最小的人插入到队列里面中。 -
每一次更新:
然后考虑
- 该元素编号大于了等待队列末尾元素。加入的话不满足上面说的性质,所以加到
want 里面去。 - 否则就可以直接加到等待队列的末尾。
最后踢掉队头元素,因为他已经被考虑过了,所以对之后的过程就没有贡献了。
时间复杂度:
代码
#include<iostream>
#include<queue>
#define pii pair<long long,int>
int n;
const int N=3e5+10;
long long a[N],m;
using namespace std;
priority_queue<int,vector<int>,greater<int>> want;//想排队但是进不来
priority_queue<pii,vector<pii>,greater<pii>> noidea;//根本就没想去排队
queue<int> wait;//在队列中了
long long now,ans[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
noidea.push({a[i],i});
}
for(int i=1;i<=n;i++){
if(wait.empty()){//如果队列已经空了
if(want.size()){//看看有没有想要进来的
wait.push(want.top());//加入编号最小的人
want.pop();//弹出
}else{//没有想要进来的人
wait.push(noidea.top().second);//硬拉一个不想来的人
now=noidea.top().first;
noidea.pop();
}
}
now+=m;
ans[wait.front()]=now;//时间
while(noidea.size()&&now>=noidea.top().first){//是否到了排队的时候
if(wait.back()>noidea.top().second){//看看满不满足单调性
wait.push(noidea.top().second);
}else{
want.push(noidea.top().second);
}
noidea.pop();//考虑完了就删除。
}
wait.pop();//踢掉
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';//输出时间
}
}