传送门

事实上就是模拟

搞一个优先队列维护一下事件结构体:时间,人的编号,入队还是出队

再维护两个 $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;
}
01-15 20:26