P9588 队列

省流:队列 + multiset + 二分 + 树状数组

分析、实现

约定本文中子序列含义为题目队列中一段连续的元素。

我们很容易得到一种思路:由于每次插入的子序列中元素一定连续,因此我们只需要在队列中记录这个数列的最小值 $l$ 和最大值 $r$ 即可,使用结构体或 pair。这样这个子序列的元素个数 $size$ 就可以表示为 $r-l+1$。

对于操作二,从队头向后进行扫描出队,同时将总数 $x$ 减去已出队的子序列的 $size$。对于最后一个子序列,仅增加 $l$ 值。

对于操作三,这样就无需逐个元素扫描而可以直接跳跃。

对于操作四,在操作一、二中对每次发生变化的子序列最大值进行维护即可(下方代码中使用了 multiset)。

优化

可惜,这个思路是对的,时间复杂度是错的(75 pts)。对于每个操作三,时间复杂度达到了线性级别。问题在于,如果数据中出现了大量连续的操作三,我们每次都要从头开始扫描,这就造成了浪费。

不难发现,如果我们能维护子序列 $size$ 的前缀和,我们就可以通过二分来对查询位置 $z$ 进行查找。下方的代码中使用了树状数组进行维护,单次查询在 $\log^2$ 级别。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=2e5+10;

struct S
{
    int l,r;
}q[MAXN*2];

int head=1,tail=1;
int BIT[MAXN];
multiset<int>cnt;

void add(int x,int k)
{
    for(int i=x;i<=2e5;i+=i&(-i))BIT[i]+=k;
}

int query(int x)
{
    int ret=0;
    for(int i=x;i;i-=i&(-i))ret+=BIT[i];
    return ret;
}

signed main()
{
    ios::sync_with_stdio(false);
    int c,t;
    cin>>c>>t;
    while(t--)
    {
        int op;
        int x;
        cin>>op;
        switch(op)
        {
            case 1:
            {
                cin>>x;
                add(tail,x);
                q[tail++]={1,x};
                cnt.insert(x);
                break;
            }
            case 2:
            {
                cin>>x;
                while(x>0)
                {
                    if(x>=q[head].r-q[head].l+1)
                    {
                        x-=(q[head].r-q[head].l+1);
                        add(head,-(q[head].r-q[head].l+1));
                        cnt.erase(cnt.find(q[head].r));
                        head++;
                    }
                    else
                    {
                        q[head].l+=x;
                        add(head,-x);
                        break;
                    }
                }
                break;
            }
            case 3:
            {
                cin>>x;
                int l=head,r=tail;
                while(l<r)
                {
                    int mid=l+r>>1;
                    if(query(mid)<x)l=mid+1;
                    else r=mid;
                }
                cout<<q[l].l+x-query(l-1)-1<<'\n';
                break;
            }
            case 4:
            {
                cout<<*(--cnt.end())<<'\n';
                break;
            }
        }
    }
    return 0;
}
posted @ 2023-08-28 13:43  H2ptimize  阅读(20)  评论(0)    收藏  举报  来源