专栏文章
【MX-S7-T3】「SMOI-R2」Monotonic Queue 题解
P11325题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqyc48g
- 此快照首次捕获于
- 2025/12/04 12:44 3 个月前
- 此快照最后确认于
- 2025/12/04 12:44 3 个月前
我们注意到,题目给出的这个代码只会在尾部退队的时候才会把 统计到 里面,并且会从 开始到 把所有元素遍历一遍,因此 可以决定哪些数可以被退掉。
但与此同时,我们还会注意到, 可以让底部的一些元素提前出队,因此考虑维护一个单调栈,枚举 ,找最优的一个到栈顶的连续区段,使得用 去退栈时答案最优,因为如果设我要退的位置为 ,我可以用区间 直接把 之前的数全部从队首退掉而不加入 。
但特别的,如果当前枚举的这个 无法使得单调栈退栈,应当直接继承上一位的答案。
同时,我们还会发现,如果我要退掉单调栈中的数 ,那么在 上方出现过的所有数都会被计入 ,因为用 退队是自下而上退的, 一旦存在其上方的数也一定存在,故上方的数不是被更上方的数退掉就是被 退掉,而这都会计入贡献,因此每向单调栈中加入一个数 ,栈内所有答案都要加上一个对应的 。
因此,我们需要维护单调栈内每一个元素的权值 以及从栈顶退到该元素时的 ,还要能够二分找到自底到顶第一个权值 ,并查询区间的最大 ,因此用线段树维护。
Code
CPP#include<bits/stdc++.h>
/*
注意到 sum 只有在尾部退队的时候才会被 + 到
我们尝试模拟一下这个过程
如果没有这个 l 就是在从左往右做单调栈过程中模拟一遍记录历史最大值就好了
但是有了 l 我们可以通过 l 来控制 一些位置不被加到
所以相当于 找到单调栈中最底部的位置 选择一个到栈顶的连续区段来 +
然后正常单调栈删除末尾
但是 要删除一个数 在这个数上方的数都一定被删了 所以要动态的把这些数的贡献弄进答案里
如果一个数都退不掉 那么我们同时记录一个 lasans 来表示上一次操作的答案 继承它即可
因此我们需要一个数据结构实现以下操作:
1.区间加 2.二分 3.区间覆盖 4.单点插入
显然线段树
*/
using LL=long long;
LL read()
{
char ch=getchar();
LL x=0;
int f=0;
while(ch<'0'||ch>'9')
f=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9')
x=(x<<3)+(x<<1)+(ch&15),ch=getchar();
return f?~x+1:x;
}
const int N=5e5+5;
const LL inf=1e18;
int n,a[N];
LL c[N];
struct node{
int mnA,clr;
LL val,plus;
void add(LL x)
{
val+=x;
plus+=x;
}
void clear()
{
clr=1;
plus=val=mnA=0;
}
};
#define lson rt<<1
#define rson rt<<1|1
node tree[N<<2];
void pushdown(int rt)
{
if(tree[rt].clr)
{
tree[lson].clear();
tree[rson].clear();
tree[rt].clr=0;
}
if(tree[rt].plus)
{
tree[lson].add(tree[rt].plus);
tree[rson].add(tree[rt].plus);
tree[rt].plus=0;
}
}
void update(int rt)
{
tree[rt].mnA=std::min(tree[lson].mnA,tree[rson].mnA);
tree[rt].val=std::max(tree[lson].val,tree[rson].val);
}
void add(int rt,int l,int r,int L,int R,LL x)
{
if(l>R||r<L)
return;
if(L<=l&&r<=R)
{
tree[rt].add(x);
return;
}
pushdown(rt);
int mid=l+r>>1;
add(lson,l,mid,L,R,x);
add(rson,mid+1,r,L,R,x);
update(rt);
}
void clear(int rt,int l,int r,int L,int R)
{
if(l>R||r<L)
return;
if(L<=l&&r<=R)
{
tree[rt].clear();
return;
}
pushdown(rt);
int mid=l+r>>1;
clear(lson,l,mid,L,R);
clear(rson,mid+1,r,L,R);
update(rt);
}
void insert(int rt,int l,int r,int Tar,int AA,LL V)
{
if(l>Tar||r<Tar)
return;
if(l==r)
{
tree[rt].mnA=AA;
tree[rt].val=V;
return;
}
pushdown(rt);
int mid=l+r>>1;
insert(lson,l,mid,Tar,AA,V);
insert(rson,mid+1,r,Tar,AA,V);
update(rt);
}
int get_Lbound(int rt,int l,int r,int AA)
{
if(tree[rt].mnA>AA)
return -1;
if(l==r)
return l;
int mid=l+r>>1;
pushdown(rt);
if(tree[lson].mnA<=AA)
return get_Lbound(lson,l,mid,AA);
else
return get_Lbound(rson,mid+1,r,AA);
}
LL max(int rt,int l,int r,int L,int R)
{
if(l>R||r<L)
return -inf;
if(L<=l&&r<=R)
return tree[rt].val;
pushdown(rt);
int mid=l+r>>1;
return std::max(max(lson,l,mid,L,R),max(rson,mid+1,r,L,R));
}
int top;
LL lasans,ans=-inf,tmp;
int main()
{
// freopen("queue.in","r",stdin);
// freopen("queue.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
c[i]=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
{
int pos=get_Lbound(1,1,n,a[i]);
if(pos>top)//单调栈内一个也退不掉
++top;
else//最少要吃到一位 不可能清空
{
lasans=max(1,1,n,pos,top);
clear(1,1,n,pos,top);
top=pos;
}
ans=std::max(ans,lasans);
insert(1,1,n,top,a[i],lasans);
add(1,1,n,1,top,c[i]);
}
printf("%lld",ans);
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...