事实上就是模拟
搞一个优先队列维护一下事件结构体:时间,人的编号,入队还是出队
再维护两个 $set$ ,队列内的人 $inQueue$ ,想要进入队列内的人 $want$
然后模拟模拟模拟!
初始把所有入队事件塞到优先队列,顺便维护一下当前最后一个取完水的时刻
每次取出优先队列里面时间最小的,时间相同优先取入队的,同时间都入队优先取编号小的
然后如果是入队,那么看看当前队列内编号最小的比较一下编号,然后根据比较结果看看是直接入队还是先塞到 $want$ 里面
如果是出队,直接更新一下答案和队列,然后看看 $want$ 里面有没有人能入队
然后就做完了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
const int N=2e5+7;
int n,P;
ll ans[N];
struct dat {
ll tim; int id; bool flag;
dat (ll _tim=0,int _id=0,bool _flag=0) { tim=_tim,id=_id,flag=_flag; }
inline bool operator < (const dat &tmp) const {
if(tim!=tmp.tim) return tim>tmp.tim;
return flag!=tmp.flag ? flag>tmp.flag : id>tmp.id;
}
}D[N];
priority_queue <dat> Q;
set <int> inQ,wnt;
int main()
{
n=read(),P=read();
for(int i=1;i<=n;i++) D[i]=dat(read(),i,0);
for(int i=1;i<=n;i++) Q.push(D[i]);
ll las=0;
while(!Q.empty())
{
dat t=Q.top(); Q.pop();
if(!t.flag)
{
if(inQ.empty()||*inQ.begin()>t.id)
{
las=max(las,t.tim),Q.push(dat(las+P,t.id,1));
inQ.insert(t.id); las+=P;
}
else wnt.insert(t.id);
continue;
}
ans[t.id]=t.tim; inQ.erase(t.id);
if(!wnt.empty() && (inQ.empty()||*inQ.begin()>*wnt.begin()))
{
auto p=wnt.begin(); inQ.insert(*p);
Q.push(dat(las+P,*p,1)); las+=P; wnt.erase(p);
}
}
for(int i=1;i<=n;i++) printf("%lld ",ans[i]); puts("");
return 0;
}
知识兔