Luogu P9588 队列 题解

P9588 队列

考虑转化问题,将原问题转化为一个长度为 q 的序列。序列中 x 表示一段 1x 的区间。

操作 1

每次增加时,输入 x,在数组末尾添加一个元素 x,并记录时间 t 和前缀和 s。时间表示这个元素在第 t 个增加操作增加。由于操作 4 需要求最大值,考虑维护一个单调队列,并执行一次单调队列队尾入队操作,权值为 x,时间为 t

操作 2

维护一个 now 表示现在已经删除了多少元素,维护一个 l 表示删除之后序列剩余的元素所处的区间的时间。输入 x,令 now 增加 x,并逐步向后比较 now 与前缀和 s 的值。若大于,说明这一段已经被删除,右移 l

操作 3

考虑在原序列上二分。由于有删除,所以查询当前的 x 元素实际上就是查询历史第 x+now 个元素。二分,找到第一个大于 x+now 的前缀和 s,则这个值必然落在这个区间中。利用 sx+now 的差值求出所查询的元素离段尾的距离,然后由段尾元素推回来即可。

操作 4

考虑单调队列。所有时间小于 l 的元素都需要出队,因为它们已经被删除。然后取队首,就是最大值。

时间复杂度 O(nlogn),瓶颈是操作 3

#include <bits/stdc++.h>
using namespace std;
struct val
{
	long long v,t;
}que[300000];
long long c,q,op,x,t,l=1,now,a[300000],s[300000],ql=1,qr=0;
void add()
{
	scanf("%lld",&x);
	a[++t]=x,s[t]=s[t-1]+x;
	while(x>=que[qr].v&&ql<=qr)qr--;
	que[++qr].v=x,que[qr].t=t;
}

void del()
{
	scanf("%lld",&x);
	now+=x;
	while(now>=s[l]&&l<=t)l++;
}

void getnum()
{
	scanf("%lld",&x);
	x+=now;
	long long bl=l,br=t;
	while(bl<br)
	   {
	   	long long mid=(bl+br)/2;
	   	if(x<=s[mid])br=mid;
	   	else bl=mid+1;
	   }
	printf("%lld\n",a[br]-s[br]+x);
}

void getmax()
{
	while(que[ql].t<l&&ql<=qr)ql++;
	printf("%lld\n",que[ql].v);
}

int main()
{
	scanf("%lld%lld",&c,&q);
	for(int i=1;i<=q;i++)
	    {
	    	scanf("%lld",&op);
	    	if(op==1)add();
	    	else if(op==2)del();
	    	else if(op==3)getnum();
	    	else if(op==4)getmax();
	    }
	return 0;
}
posted @   w9095  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示