Luogu P9588 队列 题解
考虑转化问题,将原问题转化为一个长度为 的序列。序列中 表示一段 的区间。
操作
每次增加时,输入 ,在数组末尾添加一个元素 ,并记录时间 和前缀和 。时间表示这个元素在第 个增加操作增加。由于操作 需要求最大值,考虑维护一个单调队列,并执行一次单调队列队尾入队操作,权值为 ,时间为 。
操作
维护一个 表示现在已经删除了多少元素,维护一个 表示删除之后序列剩余的元素所处的区间的时间。输入 ,令 增加 ,并逐步向后比较 与前缀和 的值。若大于,说明这一段已经被删除,右移 。
操作
考虑在原序列上二分。由于有删除,所以查询当前的 元素实际上就是查询历史第 个元素。二分,找到第一个大于 的前缀和 ,则这个值必然落在这个区间中。利用 与 的差值求出所查询的元素离段尾的距离,然后由段尾元素推回来即可。
操作
考虑单调队列。所有时间小于 的元素都需要出队,因为它们已经被删除。然后取队首,就是最大值。
时间复杂度 ,瓶颈是操作 。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探