P9588 队列 题解

· · 题解

(由于作者太菜了,所以赛时“发明”了双堆法,厉害吧 qwq)

代码(附注释):

#include <bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read() //快读
{
    char ch;
    ll x=0,f=1;
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            f=-1;
    for(; isdigit(ch);ch=getchar())
        x*=10,x+=(ch-'0');
    return x*f; 
}
int c,Q;
priority_queue < int > q; //原优先队列
priority_queue < int > p; //“缓冲区”
int now=1; //头指针
int tot; //尾指针
int a[200005]; //记录存放序列的x
int b[200005]; //记录当前序列已经被取走多少个数
ll f[200005]; //前缀和统计操作1录入的序列
ll anss; //记录操作2已经弹出的数字个数
int main()
{
    c=read();
    Q=read();
    int x,y;
    for(int i=1;i<=Q;i++)
    {
        x=read();
        if(x!=4) y=read(); //操作4不用读y
        if(x==1)
        {
            tot++;
            a[tot]=y;
            b[tot]=y;
            q.push(y); //将新序列加入原优先队列
            f[tot]=f[tot-1]+y; //前缀和
        }
        if(x==2)
        {
            anss+=y; //记录已经被弹出的数字个数
            while(y>0)
                if(y>=b[now]) 
                {
                    y-=b[now]; //虚晃一枪
                    p.push(a[now]); //将弹出序列存入“缓冲区”
                    now++; //指针移动
                }
                else
                {
                    b[now]-=y; //没弹完
                    break;      
                }
        }
        if(x==3)
        {
            int l=now,r=tot;
            int mid;
            if(y<=b[now]) //当前序列已经足够弹
            {
                printf("%d\n",a[now]-b[now]+y);
                continue;
            }
            while(l<=r) //二分查找
            {
                mid=(l+r)/2;
                if(f[mid]-anss>y) r=mid-1;
                if(f[mid]-anss<y) l=mid+1;
                if(y>f[mid-1]-anss&&y<=f[mid]-anss)
                    break; //找到查询区间
            }
            printf("%d\n",y-(f[mid-1]-anss));
        }
        if(x==4)
        {
            while((!p.empty())&&(!q.empty())&&p.top()==q.top()) 
                p.pop(),q.pop(); //比翼双飞
            printf("%d\n",q.top()); //最后得到的原优先队列的top就是答案
        }
    }
    return 0;
}