题目链接
【模板】线段树 3
题目背景
本题是线段树维护区间最值操作与区间历史最值的模板。
题目描述
给出一个长度为 n 的数列 A,同时定义一个辅助数组 B,B 开始与 A 完全相同。接下来进行了 m 次操作,操作有五种类型,按以下格式给出:
1 l r k
:对于所有的 i∈[l,r],将 Ai 加上 k(k 可以为负数)。
2 l r v
:对于所有的 i∈[l,r],将 Ai 变成 min(Ai,v)。
3 l r
:求 ∑ri=lAi。
4 l r
:对于所有的 i∈[l,r],求 Ai 的最大值。
5 l r
:对于所有的 i∈[l,r],求 Bi 的最大值。
在每一次操作后,我们都进行一次更新,让 Bi←max(Bi,Ai)。
输入格式
第一行包含两个正整数 n,m,分别表示数列 A 的长度和操作次数。
第二行包含 n 个整数 A1,A2,⋯,An,表示数列 A。
接下来 m 行,每行行首有一个整数 op,表示操作类型;接下来两个或三个整数表示操作参数,格式见【题目描述】。
输出格式
对于 op∈{3,4,5} 的操作,输出一行包含一个整数,表示这个询问的答案。
样例 #1
样例输入 #1
样例输出 #1
提示
样例说明 #1
操作次数 |
输入内容 |
操作 |
数列 |
输出结果 |
0 |
|
|
1,2,3,4,5 |
|
1 |
3 2 5 |
求出 [2,5] 所有数的和 |
1,2,3,4,5 |
14 |
2 |
1 1 3 3 |
将 [1,3] 内所有数加 3 |
4,5,6,4,5 |
|
3 |
4 2 4 |
求出 [2,4] 所有数的最大值 |
4,5,6,4,5 |
6 |
4 |
2 3 4 1 |
将 [3,4] 所有数与 1 取最小值 |
4,5,1,1,5 |
|
5 |
5 1 5 |
求出 [1,5] 所有位置历史最大值的最大值 |
4,5,1,1,5 |
6 |
6 |
3 1 4 |
求出 [1,4] 所有数的和 |
4,5,1,1,5 |
11 |
数据规模与约定
- 对于测试点 1,2,满足 n,m≤5000;
- 对于测试点 3,4,满足 op∈{1,2,3,4};
- 对于测试点 5,6,满足 op∈{1,3,4,5};
- 对于全部测试数据,保证 1≤n,m≤5×105,−5×108≤Ai≤5×108,op∈[1,5],1≤l≤r≤n,−2000≤k≤2000,−5×108≤v≤5×108。
提示
本题输入量较大,请使用合理高效的读入方法。
解题思路
吉司机线段树
难点主要在于区间取 min
和区间查询历史最值:
区间取 min
可将区间整个值分为最大值和非最大值部分,设置最大值 mxa 和其次数 cnt 以及次大值 lmxa,如果区间取 min
的值 v 范围为 lmxa<v<mxa 则可将最大值 mxa 更新为 v,如果当前区间的 mxa≤v 则整个区间不用更新,否则如果 lmxa≤v 的话递归到其子树上
对于区间查询历史最值,将区间分为最大值和非最大值后,设置 4 个懒标记:mxadd、nmxadd、mxmx、nmxmx,分别表示最大值、非最大值、最大值的最大值、非最大值的最大值的添加懒标记,后面两个懒标记主要用来处理历史最值问题,当 pushdown
时要判断左右子树的最大值是否为整个区间的最大值,所以要分为最大值和非最大值部分
另外一个难点在于 pushdown
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=5e5+5,inf=0x3f3f3f3f;
int n,m,a[N];
struct Tr
{
int l,r;
int mxa,lmxa,mxb,cnt;
int mxadd,nmxadd,mxmx,nmxmx;
LL sum;
}tr[N<<2];
void pushup(int p)
{
tr[p].mxa=max(tr[p<<1].mxa,tr[p<<1|1].mxa);
tr[p].mxb=max(tr[p<<1].mxb,tr[p<<1|1].mxb);
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
if(tr[p<<1].mxa==tr[p<<1|1].mxa)
tr[p].lmxa=max(tr[p<<1].lmxa,tr[p<<1|1].lmxa),
tr[p].cnt=tr[p<<1].cnt+tr[p<<1|1].cnt;
else if(tr[p<<1].mxa>tr[p<<1|1].mxa)
tr[p].lmxa=max(tr[p<<1].lmxa,tr[p<<1|1].mxa),
tr[p].cnt=tr[p<<1].cnt;
else
tr[p].lmxa=max(tr[p<<1|1].lmxa,tr[p<<1].mxa),
tr[p].cnt=tr[p<<1|1].cnt;
}
void change(int k1,int k2,int k3,int k4,int p)
{
tr[p].sum+=1ll*k1*tr[p].cnt+1ll*k2*(tr[p].r-tr[p].l+1-tr[p].cnt);
tr[p].mxb=max(tr[p].mxb,tr[p].mxa+k3);
tr[p].mxa+=k1;
if(tr[p].lmxa!=-inf)tr[p].lmxa+=k2;
tr[p].mxmx=max(tr[p].mxmx,tr[p].mxadd+k3);
tr[p].nmxmx=max(tr[p].nmxmx,tr[p].nmxadd+k4);
tr[p].mxadd+=k1;
tr[p].nmxadd+=k2;
}
void pushdown(int p)
{
int mx=max(tr[p<<1].mxa,tr[p<<1|1].mxa);
if(mx==tr[p<<1].mxa)
change(tr[p].mxadd,tr[p].nmxadd,tr[p].mxmx,tr[p].nmxmx,p<<1);
else
change(tr[p].nmxadd,tr[p].nmxadd,tr[p].nmxmx,tr[p].nmxmx,p<<1);
if(mx==tr[p<<1|1].mxa)
change(tr[p].mxadd,tr[p].nmxadd,tr[p].mxmx,tr[p].nmxmx,p<<1|1);
else
change(tr[p].nmxadd,tr[p].nmxadd,tr[p].nmxmx,tr[p].nmxmx,p<<1|1);
tr[p].mxadd=tr[p].nmxadd=tr[p].mxmx=tr[p].nmxmx=0;
}
void build(int p,int l,int r)
{
tr[p]={l,r};
if(l==r)
{
tr[p].sum=tr[p].mxa=tr[p].mxb=a[l];
tr[p].cnt=1;
tr[p].lmxa=-inf;
return ;
}
int mid=l+r>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
pushup(p);
}
void modify_add(int p,int l,int r,int k)
{
if(l<=tr[p].l&&tr[p].r<=r)
{
tr[p].sum+=1ll*k*(tr[p].r-tr[p].l+1);
tr[p].mxa+=k;
tr[p].mxb=max(tr[p].mxb,tr[p].mxa);
if(tr[p].lmxa!=-inf)tr[p].lmxa+=k;
tr[p].mxadd+=k;
tr[p].nmxadd+=k;
tr[p].mxmx=max(tr[p].mxmx,tr[p].mxadd);
tr[p].nmxmx=max(tr[p].nmxmx,tr[p].nmxadd);
return ;
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
if(l<=mid)
modify_add(p<<1,l,r,k);
if(r>mid)
modify_add(p<<1|1,l,r,k);
pushup(p);
}
void modify_min(int p,int l,int r,int v)
{
if(tr[p].mxa<=v)return ;
if(l<=tr[p].l&&tr[p].r<=r&&tr[p].lmxa<v)
{
int t=v-tr[p].mxa;
tr[p].sum+=1ll*t*tr[p].cnt;
tr[p].mxadd+=t;
tr[p].mxa=v;
return ;
}
pushdown(p);
int mid=tr[p].l+tr[p].r>>1;
if(l<=mid)
modify_min(p<<1,l,r,v);
if(r>mid)
modify_min(p<<1|1,l,r,v);
pushup(p);
}
LL ask_sum(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].sum;
LL res=0;
int mid=tr[p].l+tr[p].r>>1;
pushdown(p);
if(l<=mid)res+=ask_sum(p<<1,l,r);
if(r>mid)res+=ask_sum(p<<1|1,l,r);
return res;
}
int ask_maxa(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].mxa;
int res=-inf;
int mid=tr[p].l+tr[p].r>>1;
pushdown(p);
if(l<=mid)res=max(res,ask_maxa(p<<1,l,r));
if(r>mid)res=max(res,ask_maxa(p<<1|1,l,r));
return res;
}
int ask_maxb(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].mxb;
int res=-inf;
int mid=tr[p].l+tr[p].r>>1;
pushdown(p);
if(l<=mid)res=max(res,ask_maxb(p<<1,l,r));
if(r>mid)res=max(res,ask_maxb(p<<1|1,l,r));
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int op,l,r,k,v;
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
scanf("%d",&k);
modify_add(1,l,r,k);
}
else if(op==2)
{
scanf("%d",&v);
modify_min(1,l,r,v);
}
else if(op==3)
printf("%lld\n",ask_sum(1,l,r));
else if(op==4)
printf("%d\n",ask_maxa(1,l,r));
else
printf("%d\n",ask_maxb(1,l,r));
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!