事实上就是模拟
搞一个优先队列维护一下事件结构体:时间,人的编号,入队还是出队
再维护两个 $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; }