吉如一线段树维护区间历史最值
前言
本文介绍了由吉如一神犇发明的 吉如一线段树 来解决区间历史最值问题,Orz。
关于吉老师的标签回收法戳这里
问题
洛谷 P6242 【模板】线段树 3(区间最值操作、区间历史最值)
给出一个长度为 \(n\) 的数列 \(A\),同时定义一个辅助数组 \(B\),\(B\) 开始与 \(A\) 完全相同。接下来进行了 \(m\) 次操作,操作有五种类型,按以下格式给出:
1 l r k:对于所有的 \(i\in[l,r]\),将 \(A_i\) 加上 \(k\)(\(k\) 可以为负数)。2 l r v:对于所有的 \(i\in[l,r]\),将 \(A_i\) 变成 \(\min(A_i,v)\)。3 l r:求 \(\sum_{i=l}^{r}A_i\)。4 l r:对于所有的 \(i\in[l,r]\),求 \(A_i\) 的最大值。5 l r:对于所有的 \(i\in[l,r]\),求 \(B_i\) 的最大值。
吉老师线段树
这个题目是吉老师论文中两个问题的综合,在这里把两个操作的细节实现都说说吧!
\(\mathrm{I.}\) 结构体
const int N=5e5+5;
struct seg{
ll l;
ll r;
ll v; // 区间值之和
ll m; // 区间最大值
ll mc; // 区间最大值的数量
ll sm; // 区间严格次大值
ll mlz; // 区间最大值的加法懒标记
ll olz; // 区间其他值的加法懒标记
ll hm; // 区间历史上的最大值
ll hmlz; // 在懒标记被下传前,相较于前一次的最大值变化的量的最大值
ll holz; // 在懒标记被下传前,相较于前一次的其它值变化的量的最大值
}t[N*4];
有点吓人
mc sm mlz olz 四个变量用来维护区间取 min 操作,hm hmlz holz 三个变量维护区间历史最值的操作。
在具体的实现中若没有严格次大值,我们令此时的 sm 为一个常量 NOSM,NOSM=LONG_LONG_MIN。
\(\mathrm{II.}\) 建树
void b(int p,int l,int r){
t[p].l=l;
t[p].r=r;
t[p].mlz=t[p].olz=t[p].hmlz=t[p].holz=0;
if(l==r){
t[p].v=t[p].m=t[p].hm=ori[l];
t[p].mc=1;
t[p].sm=NOSM;
return ;
}
int mid=l+r>>1;
b(p*2,l,mid);
b(p*2+1,mid+1,r);
pushup(p);
return;
}
感觉没什么好说的。
\(\mathrm{III.}\) 区间上传
void pushup(int p){
int ls=p*2;
int rs=p*2+1;
t[p].v=t[ls].v+t[rs].v;
t[p].hm=max(t[ls].hm,t[rs].hm);
if(t[ls].m==t[rs].m){
t[p].m=t[ls].m;
t[p].mc=t[ls].mc+t[rs].mc;
t[p].sm=max(t[ls].sm,t[rs].sm);
}
else{
t[p].m=max(t[ls].m,t[rs].m);
if(t[p].m==t[ls].m){
t[p].mc=t[ls].mc;
t[p].sm=max(t[ls].sm,t[rs].m);
}
else{
t[p].mc=t[rs].mc;
t[p].sm=max(t[rs].sm,t[ls].m);
}
}
return ;
}
主要逻辑是一个找严格次大值。
\(\mathrm{IV.}\) 区间下传
这不是 pushdown 函数。
一般的线段树没有这个单独的函数,因为其维护的值很少,大多只有一两行就结束,但是碰到吉老师线段树这种就需要一个了。
这也给我们优化线段树结构减少 bug 提供了一种方案。
void inp(int p, //目标区间编号
int len, //目标区间长度
ll maxlazy,ll otherlazy,ll hmaxlazy,ll hotherlazy){//那四种懒标记
t[p].hm=max(t[p].hm,t[p].m+hmaxlazy);//先历史最值类
t[p].hmlz=max(t[p].hmlz,t[p].mlz+hmaxlazy);
t[p].holz=max(t[p].holz,t[p].olz+hotherlazy);
t[p].v += maxlazy * t[p].mc + otherlazy * (len - t[p].mc);
t[p].m += maxlazy;
t[p].sm += t[p].sm == NOSM ? 0 : otherlazy;
t[p].mlz += maxlazy;//再区间取 min 类
t[p].olz += otherlazy;
}
要注意这里的操作是有严格顺序的,不能改变先 区间历史最值类 再 区间取 min 类的顺序。
原理也很好理解,由于父区间挡下了所有的操作,对应代码每一行(仔细理解):
- 自己被这些操作影响历史上的最大值就是父区间历史上变化的最大值加上自己的最大值。
- 自己历史上最大值变化的最大值就是自己之前最大值变化值(不是历史上的)加上父区间最大值历史上变化的最大值。
- 自己历史上其他变化的最大值就是自己之前其他值变化值(不是历史上的)加上父区间其他值历史上变化的最大值。
- 自己值的变化由最大值的变化和次大值的变化组成,两者要分别乘以各自的数量。
- 自己现在的最大值为自己之前的加上父区间关于最大值变化的值。
- 自己的次大值如果以前没有就不变,如果有可以归类为非最大值,因此要改为自己之前的加上父区间关于其他值变化的值。
- 自己最大值的变化值就是简单的加上父区间最大值的变化值。
- 自己其它值的变化值就是简单的加上父区间其它值的变化值。
历史最好的一集
当然上面四个变量并不一定严格对应着我们维护的四种值,封装成一个函数更多时为了模块化代码。这个在下面有体现。
\(\mathrm{V.}\) pushdown 函数
由于我们维护的值中对最大和其它值分别做了记录,因此 pushdown 时要考虑最大值是从哪个区间里来的。
void pushdown(int p){
int ls=p*2;
int rs=p*2+1;
if(t[p].l==t[p].r)return;
ll tmp=max(t[ls].m,t[rs].m);//不能用 t[p].m 因为其已经被更新过了
int lenl=t[ls].r-t[ls].l+1;
int lenr=t[rs].r-t[rs].l+1;
ll maxlazy=t[p].mlz; t[p].mlz=0;
ll otherlazy=t[p].olz; t[p].olz=0;
ll hmaxlazy=t[p].hmlz; t[p].hmlz=0;
ll hotherlazy=t[p].holz; t[p].holz=0;
if(t[ls].m==tmp){//如果最大值从左边来
inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(ls,t[ls].r-t[ls].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
if(t[rs].m==tmp){//如果最大值从右边来
inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(rs,t[rs].r-t[rs].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
return;
}
如果区间原来的最大值从左区间来,那么这个区间的 mlz hmlz 标记就可以影响到左区间,此时使用 mlz olz hmlz holz 更新左区间,也就是调用区间下传函数。
如果区间原来的不从右区间区间来,那么这个区间的 mlz hmlz 标记对右区间根本没有影响,olz holz 才会平等的影响右区间中的每一个数,此时使用 olz olz holz holz 更新右区间。
从右区间来的情况是对称的,不再赘述。
要注意,if 语句部分一个常见错误写法如下:
if(t[ls].m==tmp){//如果最大值从左边来
inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
inp(rs,t[rs].r-t[rs].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
else if(t[rs].m==tmp){//如果最大值从右边来
inp(ls,t[ls].r-t[ls].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
错误原因是左右两个区间都可能有最大值,然后你懂的。
\(\mathrm{VI.}\) 区间加
void add(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
inp(p,t[p].r-t[p].l+1,k,k,k,k);
return ;
}
pushdown(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)add(p*2,l,r,k);
if(mid<r)add(p*2+1,l,r,k);
pushup(p);
return ;
}
给区间内的所有数都加个 \(k\),那么不论最大值还是其他值都要加,于是用 k k k k 来更新区间的数据就好了。
然后没有了,记得 pushup。
\(\mathrm{VII.}\) 区间取 min
void setmin(int p,int l,int r,ll k){
if(t[p].m<=k)return;
if(l<=t[p].l&&t[p].r<=r&&t[p].sm<k){
inp(p,t[p].r-t[p].l+1,k-t[p].m,0,k-t[p].m,0);
return;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
if(l<=mid)setmin(p*2,l,r,k);
if(mid<r)setmin(p*2+1,l,r,k);
pushup(p);
return;
}
取 min 时分三种情况。
- 区间的最大值比 \(k\) 还要小:那么此时无论区间是否属于我们要更改的区间,这个操作都不会对这个区间造成影响,于是直接 return 即可。
- 在区间属于要更改的区间的前提下,区间的最大值大于 \(k\),但是严格次大值小于 \(k\),此时我们只需对区间与最大值有关的标记进行操作。把区间的原最大值改到 \(k\),相当于把原来的值减去 \(m-k\),也就是加上 \(k-m\),于是调用
k-m0k-m0,来修改目标区间。 - 若区间不满足上面任何一条,即使区间属于要更改的区间,也进行暴力递归下传修改。
不要忘了 pushup!
剩下的区间和,最大值,历史最大值就比较普通了,直接上代码吧。
\(\mathrm{VIII.}\) 代码时间
标准码风:吉如一线段树。
#define psb push_back
#define mkp make_pair
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define NOSM LONG_LONG_MIN
#define int ll
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m;
const int N=5e5+5;
struct seg{
ll l;
ll r;
ll v;
ll m;
ll mc;
ll sm;
ll mlz;
ll olz;
ll hm;
ll hmlz;
ll holz;
}t[N*4];
ll ori[N];
void inp(int p,int len,ll maxlazy,ll otherlazy,ll hmaxlazy,ll hotherlazy){
t[p].hm=max(t[p].hm,t[p].m+hmaxlazy);
t[p].hmlz=max(t[p].hmlz,t[p].mlz+hmaxlazy);
t[p].holz=max(t[p].holz,t[p].olz+hotherlazy);
t[p].v += maxlazy * t[p].mc + otherlazy * (len - t[p].mc);
t[p].m += maxlazy;
t[p].sm += t[p].sm == NOSM ? 0 : otherlazy;
t[p].mlz += maxlazy;
t[p].olz += otherlazy;
}
void pushup(int p){
int ls=p*2;
int rs=p*2+1;
assert(ls<=(5e5+5)*20);
assert(rs<=(5e5+5)*20);
t[p].v=t[ls].v+t[rs].v;
t[p].hm=max(t[ls].hm,t[rs].hm);
if(t[ls].m==t[rs].m){
t[p].m=t[ls].m;
t[p].mc=t[ls].mc+t[rs].mc;
t[p].sm=max(t[ls].sm,t[rs].sm);
}
else{
t[p].m=max(t[ls].m,t[rs].m);
if(t[p].m==t[ls].m){
t[p].mc=t[ls].mc;
t[p].sm=max(t[ls].sm,t[rs].m);
}
else{
t[p].mc=t[rs].mc;
t[p].sm=max(t[rs].sm,t[ls].m);
}
}
return ;
}
void b(int p,int l,int r){
t[p].l=l;
t[p].r=r;
t[p].mlz=0;
t[p].olz=0;
t[p].hmlz=0;
t[p].holz=0;
if(l==r){
t[p].v=ori[l];
t[p].m=ori[l];
t[p].mc=1;
t[p].hm=ori[l];
t[p].sm=NOSM;
return ;
}
int mid=l+r>>1;
b(p*2,l,mid);
b(p*2+1,mid+1,r);
pushup(p);
return ;
}
void pushdown(int p){
int ls=p*2;
int rs=p*2+1;
if(t[p].l==t[p].r)return;
ll tmp=max(t[ls].m,t[rs].m);
int lenl=t[ls].r-t[ls].l+1;
int lenr=t[rs].r-t[rs].l+1;
ll maxlazy=t[p].mlz; t[p].mlz=0;
ll otherlazy=t[p].olz; t[p].olz=0;
ll hmaxlazy=t[p].hmlz; t[p].hmlz=0;
ll hotherlazy=t[p].holz; t[p].holz=0;
if(t[ls].m==tmp){
inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(ls,t[ls].r-t[ls].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
if(t[rs].m==tmp){
inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(rs,t[rs].r-t[rs].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
return;
}
void add(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
inp(p,t[p].r-t[p].l+1,k,k,k,k);
return ;
}
pushdown(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)add(p*2,l,r,k);
if(mid<r)add(p*2+1,l,r,k);
pushup(p);
return ;
}
void setmin(int p,int l,int r,ll k){
if(t[p].m<=k)return;
if(l<=t[p].l&&t[p].r<=r&&t[p].sm<k){
inp(p,t[p].r-t[p].l+1,k-t[p].m,0,k-t[p].m,0);
return;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
if(l<=mid)setmin(p*2,l,r,k);
if(mid<r)setmin(p*2+1,l,r,k);
pushup(p);
return;
}
ll qsum(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].v;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=0;
if(l<=mid)sub+=qsum(p*2,l,r);
if(mid<r)sub+=qsum(p*2+1,l,r);
return sub;
}
ll qcmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].m;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qcmax(p*2,l,r),sub);
if(mid<r)sub=max(qcmax(p*2+1,l,r),sub);
return sub;
}
ll qhmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].hm;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qhmax(p*2,l,r),sub);
if(mid<r)sub=max(qhmax(p*2+1,l,r),sub);
return sub;
}
signed main(){
cin>>n>>m;
rep(i,1,n){
ori[i]=rd;
}
b(1,1,n);
rep(i,1,m){
int op;
cin>>op;
if(op==1){
ll l,r,k;
l=rd;r=rd;k=rd;
add(1,l,r,k);
}
else if(op==2){
ll l,r,k;
l=rd;r=rd;k=rd;
setmin(1,l,r,k);
}
else if(op==3){
ll l,r;
l=rd;r=rd;
cout<<qsum(1,l,r)<<'\n';
}
else if(op==4){
ll l,r;
l=rd;r=rd;
cout<<qcmax(1,l,r)<<'\n';
}
else if(op==5){
ll l,r;
l=rd;r=rd;
cout<<qhmax(1,l,r)<<'\n';
}
}
return 0;
}
挺好玩的一种线段树啦,熟习原理之后其实并不难写的。
小拓展们
\(\mathrm{I.}\) 线段树维护区间历史最 min
\(\mathrm{II.}\) 线段树维护区间历史和
把代码赶出来以后就写!
(\(1\) day 后)
已老实,这俩东西调到心态爆炸。
就是看着对拍器大数据一发就错但是小数据死活拍不出错误数据的感觉,你懂的。
是调着这俩线段树跨的年(2025)。。。
事实上,如果给一个区间加线段树单独附上 线段树维护区间历史最 min,线段树维护区间历史和 都挺好写的,可谁让我非想把它们揉一起去呢?
好吧先把代码放这里了,希望我有生之年可以把它调出来。
简单说一下维护区间历史和的思路吧:就是给每个段单独附上一个操作次数和懒标记的历史版本和,主要思想是把原来的值和懒标记分离开来,然后下传什么的就很好办了(只有区间加的情况)。
接下来就写写线段树合并与分裂的东西吧。
/*
一般来说,
op=1;区间 setmin
op=2,区间加
op=3,区间求和
op=5,区间求历史最大值
op=6,区间最小值
op=7,区间最大值
op=3 在两种树里面分别代表区间历史最小值和区间历史版本和。
*/
//-------------------------------------------------------
// 数据生成器
//-------------------------------------------------------
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=7;
const int M=7;
const int MAXV=7;
ll ctr(){
return rand()%2==0?1:-1;
}
int main(){
srand(time(0));
mt19937 rnd(rand());
cout<<N<<' '<<M<<'\n';
for(int i=1;i<=N;i++){
cout<<(rnd()%MAXV)*ctr()<<' ';
}
cout<<'\n';
for(int i=1;i<=M;i++){
int op;
op=rnd()%4+1;
int l=rnd()%N+1;
int r=rnd()%N+1;
if(r<l){
swap(l,r);
}
if(op==1){
int k=(rnd()%MAXV)*ctr();
cout<<op<<' '<<l<<' '<<r<<' '<<k<<' '<<'\n';
}
else if(op==2){
int k=(rnd()%MAXV)*ctr();
cout<<op<<' '<<l<<' '<<r<<' '<<k<<' '<<'\n';
}
else{
cout<<op<<' '<<l<<' '<<r<<' '<<'\n';
}
}
return 0;
}
//-------------------------------------------------------
//暴力程序
//-------------------------------------------------------
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=10005;
ll nums[N];
ll hmin[N];
ll hmax[N];
ll hsum[N];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>nums[i];
hsum[i]=hmin[i]=hmax[i]=nums[i];
}
for(int i=1;i<=m;i++){
int op;
cin>>op;
if(op==1){
int l,r;
ll k;
cin>>l>>r>>k;
for(int j=l;j<=r;j++){
nums[j]=min(nums[j],k);
hmin[j]=min(hmin[j],nums[j]);
hmax[j]=max(hmax[j],nums[j]);
}
for(int j=1;j<=n;j++){
hsum[j]+=nums[j];
}
}
else if(op==2){
int l,r;
ll k;
cin>>l>>r>>k;
for(int j=l;j<=r;j++){
nums[j]+=k;
hmin[j]=min(hmin[j],nums[j]);
hmax[j]=max(hmax[j],nums[j]);
}
for(int j=1;j<=n;j++){
hsum[j]+=nums[j];
}
}
else if(op==3){
int l,r;
cin>>l>>r;
ll sum=0;
for(int j=l;j<=r;j++){
sum+=nums[j];
}
cout<<sum<<'\n';
}
else if(op==4){
// int l,r;
// cin>>l>>r;
// ll hmins=LONG_LONG_MAX;
// for(int j=l;j<=r;j++){
// hmins=min(hmins,hmin[j]);
// }
// cout<<hmins<<'\n';
int l,r;
cin>>l>>r;
ll hss=0;
for(int j=l;j<=r;j++){
hss+=hsum[j];
}
cout<<hss<<'\n';
}
else if(op==5){
int l,r;
cin>>l>>r;
ll hmaxs=LONG_LONG_MIN;
for(int j=l;j<=r;j++){
hmaxs=max(hmaxs,hmax[j]);
}
cout<<hmaxs<<'\n';
}
else if(op==6){
int l,r;
cin>>l>>r;
ll mins=LONG_LONG_MAX;
for(int j=l;j<=r;j++){
mins=min(mins,nums[j]);
}
cout<<mins<<'\n';
}
else if(op==7){
int l,r;
cin>>l>>r;
ll maxs=LONG_LONG_MIN;
for(int j=l;j<=r;j++){
maxs=max(maxs,nums[j]);
}
cout<<maxs<<'\n';
}
else if(op==8){
int l,r;
cin>>l>>r;
ll hss=0;
for(int j=l;j<=r;j++){
hss+=hsum[j];
}
cout<<hss<<'\n';
}
}
return 0;
}
//-------------------------------------------------------
//对拍器
//-------------------------------------------------------
#define rd read()
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int main(){
while(1){
system("gen.exe > data.in");
system("baoli.exe < data.in > stdans.out");
system("neostd.exe < data.in > ans.out");
if(system("fc ans.out stdans.out")){
cout<<"大香蕉"<<'\n';
Sleep(73357733);
}
else{
cout<<"很奇妙"<<'\n';
}
}
return 0;
}
//-------------------------------------------------------
//线段树1:区间历史最大+区间历史最小值
//-------------------------------------------------------
#define psb push_back
#define mkp make_pair
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define NOSM LONG_LONG_MIN
#define int ll
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m;
const int N=5e5+5;
struct seg{
ll l;
ll r;
ll v;
ll m;
ll minn;
ll mc;
ll sm;
ll mlz;
ll olz;
ll hm;
ll hmlz;
ll holz;
ll hmin;
ll hminlz;
}t[N*4];
ll ori[N];
void inp(int p,int len,ll maxlazy,ll otherlazy,ll hmaxlazy,ll hotherlazy,ll minlazy,ll hminlazy){
t[p].hm=max(t[p].hm,t[p].m+hmaxlazy);
t[p].hmlz=max(t[p].hmlz,t[p].mlz+hmaxlazy);
t[p].holz=max(t[p].holz,t[p].olz+hotherlazy);
t[p].hmin=min(t[p].hmin,t[p].minn+hminlazy);
t[p].hminlz=min(t[p].hminlz,t[p].olz+hminlazy);
t[p].v += maxlazy * t[p].mc + otherlazy * (len - t[p].mc);
t[p].m += maxlazy;
// if(maxonly==1){
// t[p].minn+=hminlazy;
// }
// else {
t[p].minn+=minlazy;
// }
t[p].sm += t[p].sm == NOSM ? 0 : otherlazy;
t[p].mlz += maxlazy;
t[p].olz += otherlazy;
}
void pushup(int p){
int ls=p*2;
int rs=p*2+1;
assert(ls<=(5e5+5)*20);
assert(rs<=(5e5+5)*20);
t[p].v=t[ls].v+t[rs].v;
t[p].hm=max(t[ls].hm,t[rs].hm);
t[p].hmin=min(t[ls].hmin,t[rs].hmin);
t[p].minn=min(t[ls].minn,t[rs].minn);
if(t[ls].m==t[rs].m){
t[p].m=t[ls].m;
t[p].mc=t[ls].mc+t[rs].mc;
t[p].sm=max(t[ls].sm,t[rs].sm);
}
else{
t[p].m=max(t[ls].m,t[rs].m);
if(t[p].m==t[ls].m){
t[p].mc=t[ls].mc;
t[p].sm=max(t[ls].sm,t[rs].m);
}
else{
t[p].mc=t[rs].mc;
t[p].sm=max(t[rs].sm,t[ls].m);
}
}
return ;
}
void b(int p,int l,int r){
t[p].l=l;
t[p].r=r;
t[p].mlz=0;
t[p].olz=0;
t[p].hmlz=0;
t[p].holz=0;
t[p].hminlz=0;
if(l==r){
t[p].v=ori[l];
t[p].m=ori[l];
t[p].mc=1;
t[p].hm=ori[l];
t[p].hmin=ori[l];
t[p].minn=ori[l];
t[p].sm=NOSM;
return ;
}
int mid=l+r>>1;
b(p*2,l,mid);
b(p*2+1,mid+1,r);
pushup(p);
return ;
}
void pushdown(int p){
int ls=p*2;
int rs=p*2+1;
if(t[p].l==t[p].r)return;
ll maxtmp=max(t[ls].m,t[rs].m);
int lenl=t[ls].r-t[ls].l+1;
int lenr=t[rs].r-t[rs].l+1;
ll maxlazy=t[p].mlz; t[p].mlz=0;
ll otherlazy=t[p].olz; t[p].olz=0;
ll hmaxlazy=t[p].hmlz; t[p].hmlz=0;
ll hotherlazy=t[p].holz; t[p].holz=0;
ll hminlazy=t[p].hminlz; t[p].hminlz=0;
if(t[ls].m==maxtmp){
if(t[ls].m==t[ls].minn){
inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy,maxlazy,min(maxlazy,hminlazy));
}
else inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy,otherlazy,hminlazy);
}
else{
inp(ls,t[ls].r-t[ls].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy,otherlazy,hminlazy);
}
if(t[rs].m==maxtmp){
if(t[rs].m==t[rs].minn){
inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy,maxlazy,min(maxlazy,hminlazy));
}
else inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy,otherlazy,hminlazy);
}
else{
inp(rs,t[rs].r-t[rs].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy,otherlazy,hminlazy);
}
return;
}
void add(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
inp(p,t[p].r-t[p].l+1,k,k,k,k,k,k);
return ;
}
pushdown(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)add(p*2,l,r,k);
if(mid<r)add(p*2+1,l,r,k);
pushup(p);
return ;
}
void setmin(int p,int l,int r,ll k){
if(t[p].m<=k)return;
if(l<=t[p].l&&t[p].r<=r&&t[p].sm<k&&t[p].sm!=NOSM){
inp(p,t[p].r-t[p].l+1,k-t[p].m,0,k-t[p].m,0,0,0);
return;
}
else if(l<=t[p].l&&t[p].r<=r&&t[p].sm==NOSM){
inp(p,t[p].r-t[p].l+1,k-t[p].m,0,k-t[p].m,0,k-t[p].m,k-t[p].m);
return;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
if(l<=mid)setmin(p*2,l,r,k);
if(mid<r)setmin(p*2+1,l,r,k);
pushup(p);
return;
}
ll qsum(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].v;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=0;
if(l<=mid)sub+=qsum(p*2,l,r);
if(mid<r)sub+=qsum(p*2+1,l,r);
return sub;
}
ll qhmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].hm;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qhmax(p*2,l,r),sub);
if(mid<r)sub=max(qhmax(p*2+1,l,r),sub);
return sub;
}
ll qhmin(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].hmin;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MAX;
if(l<=mid)sub=min(qhmin(p*2,l,r),sub);
if(mid<r)sub=min(qhmin(p*2+1,l,r),sub);
return sub;
}
ll qmin(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].minn;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MAX;
if(l<=mid)sub=min(qmin(p*2,l,r),sub);
if(mid<r)sub=min(qmin(p*2+1,l,r),sub);
return sub;
}
ll qmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].m;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qmax(p*2,l,r),sub);
if(mid<r)sub=max(qmax(p*2+1,l,r),sub);
return sub;
}
signed main(){
cin>>n>>m;
rep(i,1,n){
ori[i]=rd;
}
b(1,1,n);
rep(i,1,m){
int op;
op=rd;
if(op==2){
ll l,r,k;
l=rd;r=rd;k=rd;
add(1,l,r,k);
}
else if(op==1){
ll l,r,k;
l=rd;r=rd;k=rd;
setmin(1,l,r,k);
}
else if(op==4){
ll l,r;
l=rd;r=rd;
cout<<qhmin(1,l,r)<<'\n';
}
else if(op==5){
ll l,r;
l=rd;r=rd;
cout<<qhmax(1,l,r)<<'\n';
}
else if(op==6){
ll l,r;
l=rd;r=rd;
cout<<qmin(1,l,r)<<'\n';
}
else if(op==7){
ll l,r;
l=rd;r=rd;
cout<<qmax(1,l,r)<<'\n';
}
else if(op==3){
ll l,r;
l=rd;r=rd;
cout<<qsum(1,l,r)<<'\n';
}
// else if(op==5){
// ll l,r;
// l=rd;r=rd;
// cout<<qmin(1,l,r)<<'\n';
// }
}
return 0;
}
//-------------------------------------------------------
//线段树2:区间历史最大+区间历史版本和
//-------------------------------------------------------
#define psb push_back
#define mkp make_pair
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define NOSM LONG_LONG_MIN
#define int ll
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m;
const int N=5e5+5;
struct seg{
ll l;
ll r;
ll v;
ll m;
ll mc;
ll sm;
ll mlz;
ll olz;
ll hm;
ll hmlz;
ll holz;
ll mp;
ll mr;
ll ap;
ll ar;
ll cnt;
ll res;
ll ov;
}t[N*4];
ll ori[N];
void inp(int p,int len,ll maxlazy,ll otherlazy,ll hmaxlazy,ll hotherlazy){
t[p].hm=max(t[p].hm,t[p].m+hmaxlazy);
t[p].hmlz=max(t[p].hmlz,t[p].mlz+hmaxlazy);
t[p].holz=max(t[p].holz,t[p].olz+hotherlazy);
t[p].v += maxlazy * t[p].mc + otherlazy * (len - t[p].mc);
t[p].m += maxlazy;
t[p].sm += t[p].sm == NOSM ? 0 : otherlazy;
t[p].mlz += maxlazy;
t[p].olz += otherlazy;
}
void pushup(int p){
int ls=p*2;
int rs=p*2+1;
assert(ls<=(5e5+5)*20);
assert(rs<=(5e5+5)*20);
t[p].v=t[ls].v+t[rs].v;
t[p].hm=max(t[ls].hm,t[rs].hm);
t[p].res=t[ls].res+t[rs].res;
t[p].ov=t[p].v;
if(t[ls].m==t[rs].m){
t[p].m=t[ls].m;
t[p].mc=t[ls].mc+t[rs].mc;
t[p].sm=max(t[ls].sm,t[rs].sm);
}
else{
t[p].m=max(t[ls].m,t[rs].m);
if(t[p].m==t[ls].m){
t[p].mc=t[ls].mc;
t[p].sm=max(t[ls].sm,t[rs].m);
}
else{
t[p].mc=t[rs].mc;
t[p].sm=max(t[rs].sm,t[ls].m);
}
}
return ;
}
void b(int p,int l,int r){
t[p].l=l;
t[p].r=r;
t[p].mlz=0;
t[p].olz=0;
t[p].hmlz=0;
t[p].holz=0;
t[p].cnt=0;
if(l==r){
t[p].v=ori[l];
t[p].m=ori[l];
t[p].mc=1;
t[p].hm=ori[l];
t[p].res=ori[l];
t[p].ov=ori[l];
t[p].sm=NOSM;
return ;
}
int mid=l+r>>1;
b(p*2,l,mid);
b(p*2+1,mid+1,r);
pushup(p);
return ;
}
void pushdown(int p){
int ls=p*2;
int rs=p*2+1;
if(t[p].l==t[p].r)return;
ll tmp=max(t[ls].m,t[rs].m);
int lenl=t[ls].r-t[ls].l+1;
int lenr=t[rs].r-t[rs].l+1;
ll maxlazy=t[p].mlz; t[p].mlz=0;
ll otherlazy=t[p].olz; t[p].olz=0;
ll hmaxlazy=t[p].hmlz; t[p].hmlz=0;
ll hotherlazy=t[p].holz; t[p].holz=0;
ll maxpattern=t[p].mp; t[p].mp=0;
ll maxreal=t[p].mr; t[p].mr=0;
ll addpattern=t[p].ap; t[p].ap=0;
ll addreal=t[p].ar; t[p].ar=0;
ll opcnt=t[p].cnt; t[p].cnt=0;
//t[p].res+=t[p].ov*opcnt+addreal*(t[p].r-t[p].l+1)+maxreal*t[p].mc;
t[ls].mr=t[ls].mr*(max(opcnt,(ll)1))+maxreal;
t[ls].mp+=maxpattern;
t[ls].ar=t[ls].ar*(max(opcnt,(ll)1))+addreal;
t[ls].ap+=addpattern;
t[ls].cnt+=opcnt;
t[ls].res+=t[ls].ov*opcnt+addreal*(t[ls].r-t[ls].l+1)+maxreal*t[ls].mc;
t[rs].mr=t[rs].mr*(max(opcnt,(ll)1))+maxreal;
t[rs].mp+=maxpattern;
t[rs].ar=t[rs].ar*(max(opcnt,(ll)1))+addreal;
t[rs].ap+=addpattern;
t[rs].cnt+=opcnt;
t[rs].res+=t[rs].ov*opcnt+addreal*(t[rs].r-t[rs].l+1)+maxreal*t[rs].mc;
if(t[ls].m==tmp){
inp(ls,t[ls].r-t[ls].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(ls,t[ls].r-t[ls].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
if(t[rs].m==tmp){
inp(rs,t[rs].r-t[rs].l+1,maxlazy,otherlazy,hmaxlazy,hotherlazy);
}
else{
inp(rs,t[rs].r-t[rs].l+1,otherlazy,otherlazy,hotherlazy,hotherlazy);
}
return;
}
void add(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
inp(p,t[p].r-t[p].l+1,k,k,k,k);
t[p].ap+=k;
t[p].ar+=t[p].ap;
t[p].mr+=t[p].mp;
t[p].cnt++;
t[p].res+=t[p].ap*(t[p].r-t[p].l+1)+t[p].mp*t[p].mc+t[p].ov;
return ;
}
pushdown(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)add(p*2,l,r,k);
else{
t[p*2].mr+=t[p*2].mp;
t[p*2].ar+=t[p*2].ap;
t[p*2].cnt++;
t[p*2].res+=t[p*2].ap*(t[p*2].r-t[p*2].l+1)+t[p*2].mp*t[p*2].mc+t[p*2].ov;
}
if(mid<r)add(p*2+1,l,r,k);
else {
t[p*2+1].mr+=t[p*2+1].mp;
t[p*2+1].ar+=t[p*2+1].ap;
t[p*2+1].cnt++;
t[p*2+1].res+=t[p*2+1].ap*(t[p*2+1].r-t[p*2+1].l+1)+t[p*2+1].mp*t[p*2+1].mc+t[p*2+1].ov;
}
pushup(p);
return ;
}
void setmin(int p,int l,int r,ll k){
if(t[p].m<=k){
t[p].mr+=t[p].mp;
t[p].ar+=t[p].ap;
t[p].cnt++;
t[p].res+=t[p].ap*(t[p].r-t[p].l+1)+t[p].mp*t[p].mc+t[p].ov;
return;
}
if(l<=t[p].l&&t[p].r<=r&&t[p].sm<k){
t[p].mp+=k-t[p].m;
t[p].mr+=t[p].mp;
t[p].ar+=t[p].ap;
t[p].cnt++;
t[p].res+=t[p].ap*(t[p].r-t[p].l+1)+t[p].mp*t[p].mc+t[p].ov;
inp(p,t[p].r-t[p].l+1,k-t[p].m,0,k-t[p].m,0);
return;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
if(l<=mid)setmin(p*2,l,r,k);
else{
t[p*2].mr+=t[p*2].mp;
t[p*2].ar+=t[p*2].ap;
t[p*2].cnt++;
t[p*2].res+=t[p*2].ap*(t[p*2].r-t[p*2].l+1)+t[p*2].mp*t[p*2].mc+t[p*2].ov;
}
if(mid<r)setmin(p*2+1,l,r,k);
else {
t[p*2+1].mr+=t[p*2+1].mp;
t[p*2+1].ar+=t[p*2+1].ap;
t[p*2+1].cnt++;
t[p*2+1].res+=t[p*2+1].ap*(t[p*2+1].r-t[p*2+1].l+1)+t[p*2+1].mp*t[p*2+1].mc+t[p*2+1].ov;
}
pushup(p);
return;
}
ll qsum(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].v;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=0;
if(l<=mid)sub+=qsum(p*2,l,r);
if(mid<r)sub+=qsum(p*2+1,l,r);
return sub;
}
ll qhmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].hm;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qhmax(p*2,l,r),sub);
if(mid<r)sub=max(qhmax(p*2+1,l,r),sub);
return sub;
}
//ll qmin(int p,int l,int r){
// if(l<=t[p].l&&t[p].r<=r){
// return t[p].minn;
// }
// int mid=t[p].l+t[p].r>>1;
// pushdown(p);
// ll sub=LONG_LONG_MAX;
// if(l<=mid)sub=min(qmin(p*2,l,r),sub);
// if(mid<r)sub=min(qmin(p*2+1,l,r),sub);
//
// return sub;
//}
ll qmax(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].m;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=LONG_LONG_MIN;
if(l<=mid)sub=max(qmax(p*2,l,r),sub);
if(mid<r)sub=max(qmax(p*2+1,l,r),sub);
return sub;
}
ll qhsum(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].res;
}
int mid=t[p].l+t[p].r>>1;
pushdown(p);
ll sub=0;
if(l<=mid)sub+=qhsum(p*2,l,r);
if(mid<r)sub+=qhsum(p*2+1,l,r);
return sub;
}
signed main(){
cin>>n>>m;
rep(i,1,n){
ori[i]=rd;
}
b(1,1,n);
rep(i,1,m){
int op;
cin>>op;
if(op==2){
ll l,r,k;
l=rd;r=rd;k=rd;
add(1,l,r,k);
}
else if(op==1){
ll l,r,k;
l=rd;r=rd;k=rd;
setmin(1,l,r,k);
}
else if(op==3){
ll l,r;
l=rd;r=rd;
cout<<qsum(1,l,r)<<'\n';
}
else if(op==4){
ll l,r;
l=rd;r=rd;
cout<<qhsum(1,l,r)<<'\n';
}
else if(op==5){
ll l,r;
l=rd;r=rd;
cout<<qhmax(1,l,r)<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号