[学习笔记]各种口味的线段树(一)

壹. 平凡的线段树

考虑一个数列 A=10,11,12,13,14
线段树之所以称为“树”,是因为其具有树的结构特性
线段树本身是专门用来处理区间问题的
对于每一个线段树的子节点而言,都表示整个序列中的一段子区间;对于每个叶子节点而言,都表示序列中的单个元素信息
子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。

image

线段树就是分块思想的树化,或者说是对于信息处理的二进制化
通过将整个序列分为有穷个小块,对于要查询的一段区间,总是可以整合成 k 个所分块与 m 个单个元素的信息的并
线段树可以通过类似于二分的修改、查询操作让这两个复杂度都变成 O(logn)

然而我们发现如果在每次操作时都硬更新时间复杂度会达到恐怖的 O(nlogn) ,所以引入一个叫做 懒标记 的玩意,每次逐级下放达到目的

当然这篇博客不是讲普通线段树的,因此请移步 皎月半洒花巨佬的洛谷日报

但为了我个人复习用还是要丢一个模板上来

P3372 【模板】线段树 1

居然挂了两发我是不是要凉了

点击查看代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#define WR WinterRain
#define int long long
using namespace std;
const int WR=1001000,INF=1099511627776;
struct SegmentTree{
    int l,r,val,lzy;
}tree[WR<<2];
int n,m;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].val=tree[k<<1].val+tree[k<<1|1].val;
}
void pushdown(int k){//下放懒标记
    tree[k<<1].val+=(tree[k<<1].r-tree[k<<1].l+1)*tree[k].lzy;
    tree[k<<1|1].val+=(tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].lzy;
    tree[k<<1].lzy+=tree[k].lzy;
    tree[k<<1|1].lzy+=tree[k].lzy;
    tree[k].lzy=0;
}
void build(int k,int l,int r){
    tree[k].l=l,tree[k].r=r;
    if(l==r){
        tree[k].val=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify(int k,int l,int r,int val){//区间修改
    if(tree[k].l>=l&&tree[k].r<=r){
        tree[k].val+=(tree[k].r-tree[k].l+1)*val;
        tree[k].lzy+=val;//注意是+=
        return;
    }
    if(tree[k].lzy) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify(k<<1,l,r,val);
    if(r>mid) modify(k<<1|1,l,r,val);
    pushup(k);
}
int query(int k,int l,int r){//区间查询
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].val;
    }
    if(tree[k].lzy) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=0;
    if(l<=mid) res+=query(k<<1,l,r);
    if(r>mid) res+=query(k<<1|1,l,r);
    return res;
}
signed main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==1){
            int l=read(),r=read(),val=read();
            modify(1,l,r,val);
        }else{
            int l=read(),r=read();
            printf("%lld\n",query(1,l,r));
        }
    }
    return 0;
}

P3373 【模板】线段树 2

需要维护两个懒标记:一个记录乘法,一个记录加法

点击查看代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#define WR WinterRain
#define int long long
using namespace std;
const int WR=101000,INF=1099511627776;
struct SegmentTree{
    int l,r,val,mullzy,addlzy;
    SegmentTree(){l=r=val=addlzy=0,mullzy=1;}
}tree[WR<<2];
int n,m,mod;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].val=(tree[k<<1].val+tree[k<<1|1].val)%mod;
}
void pushdown(int k){
    tree[k<<1].val=(tree[k<<1].val*tree[k].mullzy%mod+tree[k].addlzy*(tree[k<<1].r-tree[k<<1].l+1)%mod)%mod;
    tree[k<<1|1].val=(tree[k<<1|1].val*tree[k].mullzy%mod+tree[k].addlzy*(tree[k<<1|1].r-tree[k<<1|1].l+1)%mod)%mod;
    tree[k<<1].mullzy=tree[k<<1].mullzy*tree[k].mullzy%mod;
    tree[k<<1|1].mullzy=tree[k<<1|1].mullzy*tree[k].mullzy%mod;
    tree[k<<1].addlzy=(tree[k<<1].addlzy*tree[k].mullzy%mod+tree[k].addlzy)%mod;
    tree[k<<1|1].addlzy=(tree[k<<1|1].addlzy*tree[k].mullzy%mod+tree[k].addlzy)%mod;
    tree[k].mullzy=1,tree[k].addlzy=0;
}
void build(int k,int l,int r){
    tree[k].l=l,tree[k].r=r;
    if(l==r){
        tree[k].val=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify_mul(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        tree[k].val=tree[k].val*val%mod;
        tree[k].mullzy=tree[k].mullzy*val%mod;
        tree[k].addlzy=tree[k].addlzy*val%mod;
        return;
    }
    if(tree[k].mullzy!=1||tree[k].addlzy!=0) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_mul(k<<1,l,r,val);
    if(r>mid) modify_mul(k<<1|1,l,r,val);
    pushup(k);
}
void modify_add(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        tree[k].val=(tree[k].val+(tree[k].r-tree[k].l+1)*val%mod)%mod;
        tree[k].addlzy=(tree[k].addlzy+val)%mod;
        return;
    }
    if(tree[k].mullzy!=1||tree[k].addlzy!=0) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_add(k<<1,l,r,val);
    if(r>mid) modify_add(k<<1|1,l,r,val);
    pushup(k);
}
int query(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].val;
    }
    if(tree[k].mullzy!=1||tree[k].addlzy!=0) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=0;
    if(l<=mid) res=(res+query(k<<1,l,r))%mod;
    if(r>mid) res=(res+query(k<<1|1,l,r))%mod;
    return res;
}
signed main(){
    n=read(),m=read(),mod=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==1){
            int l=read(),r=read(),val=read();
            modify_mul(1,l,r,val);
        }else if(opt==2){
            int l=read(),r=read(),val=read();
            modify_add(1,l,r,val);
        }else{
            int l=read(),r=read();
            printf("%lld\n",query(1,l,r));
        }
    }
    return 0;
}

然后就可以挑战一道叫做 山海经 的有趣题目了
其实不难,就是需要求出区间内最大子段和
pushup 函数里只需要维护 10 个变量,简直[数据删除]!

P6242 【模板】线段树 3

这不还是模板题么,看我秒切 个头啊!!!
好的,我们引入下一个主题

贰. 吉司机线段树

吉司机线段树就是维护区间最值和区间历史最值的线段树,来源于吉如一巨佬
看这道模板题:
给你一个长度为 n 的序列 a ,令序列 b=a
现在让你进行 m 次操作,分为 5 种:

  1. 1 l r v:将序列 a 中区间 [l,r] 的数加上 kkk
  2. 2 l r v:将序列 a 中区间 [l,r] 的数对 vvv 取最小值
  3. 3 l r:求序列 a 中区间 [l,r] 的数的和
  4. 4 l r:求序列 a 中区间 [l,r] 的数的最大值
  5. 5 l r:求序列 b 中区间 [l,r] 的数的最大值

每次操作后令 bi=max(bi,ai)

好像很迷惑?那我们从最简单的开始分析

一. 单纯的区间求最值操作

首先假设有一个序列 a
区间最值操作是指给定 l,r,val ,将所有 i[l,r]aivalmin(或者 max)
考虑一道题目

HDU5306 Gorgeous Sequence 别点了,HDU 崩了

请你维护一个序列 a ,支持 3 种操作
1. 给定 l,r,val ,对于所有 i[l,r]ai ,将其变为 min(ai,val)
2. 给定 l,r ,求区间最大值
3. 给定 l,r ,求 i=lrai

考虑到区间取 min 的操作只会对最大值超过 k 的节点产生影响,我们可以在这方面找一想法
线段树的一个节点需要维护四个信息:
区间和 val ,区间最大值 maxval ,区间严格次大值 sndmax 和最大值的个数 maxcnt
那么,一次区间最值操作作用在这个节点上时,可以被分为以下三种情况:

  1. valmaxval 显然没有任何作用,直接返回
  2. sndmax<val<maxval 此时该节点代表区间的最大值被修改为 val ,区间和加上 maxcnt×(maxvalval),打个懒标记回溯即可
  3. valsndmax 此时无法快速更新区间信息,因此需要继续递归到左右子树中,回溯时合并信息

举个例子,现在要以 k=2 对下面这棵线段树维护的区间取 min
每个节点左侧表示区间最大值,右侧表示严格次大值。

按照上面描述的操作,我们应该沿着红色的边 DFS,最后在红色的节点上更新区间和并打上标记。

点击查看代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1001000,INF=1099511627776;
struct SegmentTree{
    int l,r,maxval,sndmax,maxcnt,val,lzy;
}tree[WR<<2];
int n,m;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].val=tree[k<<1].val+tree[k<<1|1].val;
    if(tree[k<<1].maxval==tree[k<<1|1].maxval){//如果左右子树最大值相等 
        tree[k].maxval=tree[k<<1].maxval; 
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].sndmax);//次大值取左右子树次大值的最大值 
        tree[k].maxcnt=tree[k<<1].maxcnt+tree[k<<1|1].maxcnt;
    }else if(tree[k<<1].maxval>tree[k<<1|1].maxval){//如果左子树最大值为区间最大值 
        tree[k].maxval=tree[k<<1].maxval;
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].maxval);//次大值取左子树次大值和右子树最大值的最大值 
        tree[k].maxcnt=tree[k<<1].maxcnt;
    }else{
        tree[k].maxval=tree[k<<1|1].maxval;
        tree[k].sndmax=max(tree[k<<1].maxval,tree[k<<1|1].sndmax);
        tree[k].maxcnt=tree[k<<1|1].maxcnt;
    }
}
void update(int k,int val){
    if(tree[k].maxval<=val) return;
    tree[k].val-=(tree[k].maxval-val)*tree[k].maxcnt;
    tree[k].maxval=tree[k].lzy=val;
}
void pushdown(int k){
    update(k<<1,tree[k].lzy);
    update(k<<1|1,tree[k].lzy);
    tree[k].lzy=-1;//重置懒标记 
}
void build(int k,int l,int r){
    tree[k].lzy=-1,tree[k].l=l,tree[k].r=r;
    if(l==r){
        tree[k].val=tree[k].maxval=a[l];
        tree[k].sndmax=-1;
        tree[k].maxcnt=1;
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify(int k,int l,int r,int val){
    if(val>=tree[k].maxval) return;//情况 1 
    if(tree[k].l>=l&&tree[k].r<=r&&tree[k].sndmax<val){//情况 2 
        update(k,val);
        return;
    }
    if(tree[k].lzy!=-1) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;//情况 3 
    if(l<=mid) modify(k<<1,l,r,val);//继续下放 
    if(r>mid) modify(k<<1|1,l,r,val);
    pushup(k);
}
int query_max(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].maxval;
    }
    if(tree[k].lzy!=-1) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_max(k<<1,l,r));
    if(r>mid) res=max(res,query_max(k<<1|1,l,r));
    return res;
}
int query_sum(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].val;
    }
    if(tree[k].lzy!=-1) pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=0;
    if(l<=mid) res+=query_sum(k<<1,l,r);
    if(r>mid) res+=query_sum(k<<1|1,l,r);
    return res;
}
signed main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==0){
            int l=read(),r=read(),val=read();
            modify(1,l,r,val);
        }else if(opt==1){
            int l=read(),r=read();
            printf("%lld\n",query_max(1,l,r));
        }else{
            int l=read(),r=read();
            printf("%lld\n",query_sum(1,l,r));
        }
    }
    return 0;
}

二. 考虑加入加减操作

BZOJ4695最假女选手

请你维护一个序列 a ,支持 6 种操作
1. 给定 l,r,val ,对于所有 i[l,r]ai ,将其加上 val
2. 给定 l,r,val ,对于所有 i[l,r]ai ,将其变为 max(ai,val)
3. 给定 l,r,val ,对于所有 i[l,r]ai ,将其变为 min(ai,val)
4. 给定 l,r ,求 i=lrai
5. 给定 l,r ,求区间最大值
6. 给定 l,r ,求区间最小值

注意到了区间取最值同时出现了,显然地我们可以维护多一倍的标记维护新值,但相应的,
码量也会多一倍
这无疑是坏的,因此我们考虑如何缩减程序
应这道题的需要,我们将一个区间的元素划分为最大值、最小值和其他值三种
首先在每个节点上肯定要维护区间和 val ,区间最大值 maxval 和最小值 minval 的信息
同时我们也要维护次大值 sndmax ,次小值 sndmin 和最大值最小值的个数 cntmax,cntmin
我们分别讨论题目中的三种修改操作:

  1. 对于加减操作,在线段树上定位区间后直接对三类值同时加上 val
  2. 对于取最小值操作,在线段树上暴力搜索找到 sndmax<val<maxval 的节点;这些节点对应的区间的最大值都应该改成 val ,只对最大值加上 valmaxval 即可
  3. 对于取最大值操作,同理

但是有一些要注意的地方:

  1. 以最大值上的加减标记为例,下传这个标记时要判断子区间内是否包含最大值,如果不包含则应下传其他值的加减标记;
  2. 如果一个区间的值域很小,可能会发生一个值既是最大值又是次小值这种情况,这种情况要特判,分辨到底该被哪个标记作用。

为了不至于混乱,我们开 3 个懒标记记录有关数值

点击查看代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=501000,INF=1099511627776;
struct SegmentTree{
    int l,r,val,lzy;
    int maxval,sndmax,maxcnt,maxlzy;
    int minval,sndmin,mincnt,minlzy;
}tree[WR*5];
int n,m;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].val=tree[k<<1].val+tree[k<<1|1].val;
    if(tree[k<<1].maxval==tree[k<<1|1].maxval){
        tree[k].maxval=tree[k<<1].maxval;
        tree[k].maxcnt=tree[k<<1].maxcnt+tree[k<<1|1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].sndmax);
    }else if(tree[k<<1].maxval>tree[k<<1|1].maxval){
        tree[k].maxval=tree[k<<1].maxval;
        tree[k].maxcnt=tree[k<<1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].maxval);
    }else{
        tree[k].maxval=tree[k<<1|1].maxval;
        tree[k].maxcnt=tree[k<<1|1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].maxval,tree[k<<1|1].sndmax);
    }
    if(tree[k<<1].minval==tree[k<<1|1].minval){
        tree[k].minval=tree[k<<1].minval;
        tree[k].mincnt=tree[k<<1].mincnt+tree[k<<1|1].mincnt;
        tree[k].sndmin=min(tree[k<<1].sndmin,tree[k<<1|1].sndmin); 
    }else if(tree[k<<1].minval<tree[k<<1|1].minval){
        tree[k].minval=tree[k<<1].minval;
        tree[k].mincnt=tree[k<<1].mincnt;
        tree[k].sndmin=min(tree[k<<1].sndmin,tree[k<<1|1].minval);
    }else{
        tree[k].minval=tree[k<<1|1].minval;
        tree[k].mincnt=tree[k<<1|1].mincnt;
        tree[k].sndmin=min(tree[k<<1].minval,tree[k<<1|1].sndmin);
    }
}
void update(int k,int valmin,int valmax,int valsum){
    if(tree[k].minval==tree[k].maxval){
        if(valmin==valsum) valmin=valmax;
        else valmax=valmin;//在只有一个值的时候不应被其他值的标记作用
        tree[k].val+=valmin*tree[k].mincnt;//因此找那个非 0 的标记
    }else tree[k].val+=valmin*tree[k].mincnt+valmax*tree[k].maxcnt+
                       valsum*(tree[k].r-tree[k].l+1-tree[k].maxcnt-tree[k].mincnt);
    if(tree[k].sndmax==tree[k].minval) tree[k].sndmax+=valmin;
    else if(tree[k].sndmax!=-INF) tree[k].sndmax+=valsum;
    if(tree[k].sndmin==tree[k].maxval) tree[k].sndmin+=valmax;//次小值等于最大值,应该被最大值标记作用
    else if(tree[k].sndmin!=INF) tree[k].sndmin+=valsum;//否则应该被其他值标记作用
    tree[k].maxval+=valmax,tree[k].minval+=valmin;
    tree[k].maxlzy+=valmax,tree[k].minlzy+=valmin,tree[k].lzy+=valsum;
}
void pushdown(int k){
    int minn=min(tree[k<<1].minval,tree[k<<1|1].minval);
    int maxx=max(tree[k<<1].maxval,tree[k<<1|1].maxval);
    update(k<<1,tree[k<<1].minval==minn?tree[k].minlzy:tree[k].lzy,
           tree[k<<1].maxval==maxx?tree[k].maxlzy:tree[k].lzy,tree[k].lzy);
    update(k<<1|1,tree[k<<1|1].minval==minn?tree[k].minlzy:tree[k].lzy,
           tree[k<<1|1].maxval==maxx?tree[k].maxlzy:tree[k].lzy,tree[k].lzy);
    tree[k].maxlzy=tree[k].minlzy=tree[k].lzy=0;
}
void build(int k,int l,int r){
    tree[k].l=l,tree[k].r=r;
    tree[k].maxlzy=tree[k].minlzy=tree[k].lzy=0;
    if(l==r){
        tree[k].val=tree[k].maxval=tree[k].minval=a[l];
        tree[k].sndmin=INF,tree[k].sndmax=-INF;
        tree[k].maxcnt=tree[k].mincnt=1;
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify_sum(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        update(k,val,val,val);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_sum(k<<1,l,r,val);
    if(r>mid) modify_sum(k<<1|1,l,r,val);
    pushup(k);
}
void modify_min(int k,int l,int r,int val){
    if(val>=tree[k].maxval) return;
    if(tree[k].l>=l&&tree[k].r<=r&&val>tree[k].sndmax){
        update(k,0,val-tree[k].maxval,0);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_min(k<<1,l,r,val);
    if(r>mid) modify_min(k<<1|1,l,r,val);
    pushup(k);
}
void modify_max(int k,int l,int r,int val){
    if(val<=tree[k].minval) return;
    if(tree[k].l>=l&&tree[k].r<=r&&val<tree[k].sndmin){
        update(k,val-tree[k].minval,0,0);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_max(k<<1,l,r,val);
    if(r>mid) modify_max(k<<1|1,l,r,val);
    pushup(k);
}
int query_sum(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].val;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=0;
    if(l<=mid) res+=query_sum(k<<1,l,r);
    if(r>mid) res+=query_sum(k<<1|1,l,r);
    return res;
}
int query_max(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].maxval;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_max(k<<1,l,r));
    if(r>mid) res=max(res,query_max(k<<1|1,l,r));
    return res;
}
int query_min(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].minval;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=INF;
    if(l<=mid) res=min(res,query_min(k<<1,l,r));
    if(r>mid) res=min(res,query_min(k<<1|1,l,r));
    return res;
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    m=read();
    for(int i=1;i<=m;i++){
        int opt=read();
        if(opt==1){
            int l=read(),r=read(),val=read();
            modify_sum(1,l,r,val);
        }
        if(opt==2){
            int l=read(),r=read(),val=read();
            modify_max(1,l,r,val);
        }
        if(opt==3){
            int l=read(),r=read(),val=read();
            modify_min(1,l,r,val);
        }
        if(opt==4){
            int l=read(),r=read();
            printf("%lld\n",query_sum(1,l,r));
        }
        if(opt==5){
            int l=read(),r=read();
            printf("%lld\n",query_max(1,l,r));
        }
        if(opt==6){
            int l=read(),r=read();
            printf("%lld\n",query_min(1,l,r));
        }
    }
    return 0;
}

三. 区间历史最值问题

POJ3064. Tyvj1518 CPU监控

请你维护一个序列 a 和一个辅助序列 b (最开始 b=a ),支持 4 种操作
1. 给定 l,r,val ,对于所有 i[l,r]ai ,将其加上 val
2. 给定 l,r,val ,对于所有 i[l,r]ai ,将其变为 val
3. 给定 l,r ,求 a 的区间最大值
4. 给定 l,r ,求 b 的区间最大值
每次操作后,将所有 bi 变为 max(ai,bi)

考虑如何维护历史最值
显然地,我们要维护最大值 maxval ,历史最大值 hismax 和区间加标记 maxlzy
同时我们还需要维护一个 hislzy ,用来维护从上一次下传 maxlzy 到现在,区间加标记达到的最大值
考虑如何合并?

{hislzyson=max(hislzyson,maxlzyson+hislzyfa)hismaxson=max(hismaxson,maxvalson+hislzyfa)

现在加入区间覆盖操作,我们把标记换成 (maxlzy,covval) 表示将当前区间先加上 maxlzy 再全部变成 covval
这个标记是可以合并的:如果一个区间已经被覆盖成了一个值,那么区间加操作可以转化为区间覆盖操作,直接把它加到 covval 上即可。
同理,我们还需要维护 (hislzy,hiscov)
表示上一次下传这个节点的标记到现在,区间加的最大值为 hislzy ,区间覆盖的最大值为 hiscov

点击查看代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=501000,INF=1099511627776;
struct SegmentTree{
    int l,r;
    int maxval,maxlzy,covval;
    int hismax,hislzy,hiscov;
    bool tag;
}tree[WR<<2];
int n,q;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].maxval=max(tree[k<<1].maxval,tree[k<<1|1].maxval);
    tree[k].hismax=max(tree[k<<1].hismax,tree[k<<1|1].hismax);
}
void update_plus(int k,int val,int hisval){
    tree[k].hismax=max(tree[k].hismax,tree[k].maxval+hisval);
    tree[k].maxval+=val;
    if(!tree[k].tag) tree[k].hislzy=max(tree[k].hislzy,tree[k].maxlzy+hisval),tree[k].maxlzy+=val;
    else tree[k].hiscov=max(tree[k].hiscov,tree[k].covval+hisval),tree[k].covval+=val;
}
void update_cover(int k,int val,int hisval){
    tree[k].hismax=max(tree[k].hismax,hisval);
    tree[k].maxval=val;
    tree[k].hiscov=max(tree[k].hiscov,hisval);
    tree[k].covval=val;
    tree[k].tag=true;
}
void pushdown(int k){
    if(tree[k].maxlzy){
        update_plus(k<<1,tree[k].maxlzy,tree[k].hislzy);
        update_plus(k<<1|1,tree[k].maxlzy,tree[k].hislzy);
        tree[k].maxlzy=tree[k].hislzy=0;
    }
    if(tree[k].tag){
        update_cover(k<<1,tree[k].covval,tree[k].hiscov);
        update_cover(k<<1|1,tree[k].covval,tree[k].hiscov);
        tree[k].tag=false;
        tree[k].hiscov=-INF;
    }
}
void build(int k,int l,int r){
    tree[k].l=l,tree[k].r=r;
    tree[k].maxlzy=tree[k].hislzy=tree[k].tag=0;
    tree[k].hiscov=-INF;
    if(l==r){
        tree[k].maxval=tree[k].hismax=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify_plus(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        update_plus(k,val,val);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_plus(k<<1,l,r,val);
    if(r>mid) modify_plus(k<<1|1,l,r,val);
    pushup(k);
}
void modify_cover(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        update_cover(k,val,val);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_cover(k<<1,l,r,val);
    if(r>mid) modify_cover(k<<1|1,l,r,val);
    pushup(k);
}
int query_max(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].maxval;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_max(k<<1,l,r));
    if(r>mid) res=max(res,query_max(k<<1|1,l,r));
    return res;
}
int query_his(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].hismax;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_his(k<<1,l,r));
    if(r>mid) res=max(res,query_his(k<<1|1,l,r));
    return res;
}
signed main(){
//    freopen("test.in", "r", stdin);
//    freopen("cpp.out", "w", stdout);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    q=read();
    while(q--){
        char str[5];
        scanf("%s",str+1);
        if(str[1]=='Q'){
            int l=read(),r=read();
            printf("%lld\n",query_max(1,l,r));
        }
        if(str[1]=='A'){
            int l=read(),r=read();
            printf("%lld\n",query_his(1,l,r));
        }
        if(str[1]=='P'){
            int l=read(),r=read(),val=read();
            modify_plus(1,l,r,val);
        }
        if(str[1]=='C'){
            int l=read(),r=read(),val=read();
            modify_cover(1,l,r,val);
        }
    }
    return 0;
}

四. 加入区间最值操作

终于到了我们的模板
P6242 【模板】线段树 3
考虑维护 4 个标记

  1. 最大值加减标记 maxlzy
  2. 最大值历史最大的加减标记 hismaxlzy
  3. 非最大值加减标记 lzy
  4. 非最大值历史最大的加减标记 hislzy

然后结合上面的内容就可以比较轻易地做出来

点击查看代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=501000,INF=1099511627776;
struct SegmentTree{
    int l,r,val,lzy;
    int maxval,sndmax,maxcnt,maxlzy;
    int hismax,hismaxlzy,hislzy;
}tree[WR<<2];
int n,q;
int a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void pushup(int k){
    tree[k].val=tree[k<<1].val+tree[k<<1|1].val;
    tree[k].hismax=max(tree[k<<1].hismax,tree[k<<1|1].hismax);
    if(tree[k<<1].maxval==tree[k<<1|1].maxval){
        tree[k].maxval=tree[k<<1].maxval;
        tree[k].maxcnt=tree[k<<1].maxcnt+tree[k<<1|1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].sndmax);
    }else if(tree[k<<1].maxval>tree[k<<1|1].maxval){
        tree[k].maxval=tree[k<<1].maxval;
        tree[k].maxcnt=tree[k<<1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].sndmax,tree[k<<1|1].maxval);
    }else{
        tree[k].maxval=tree[k<<1|1].maxval;
        tree[k].maxcnt=tree[k<<1|1].maxcnt;
        tree[k].sndmax=max(tree[k<<1].maxval,tree[k<<1|1].sndmax);
    }
}
void update(int k,int lzy,int hislzy,int maxlzy,int hismaxlzy){
    tree[k].val+=maxlzy*tree[k].maxcnt+lzy*(tree[k].r-tree[k].l+1-tree[k].maxcnt);
    tree[k].hismax=max(tree[k].hismax,tree[k].maxval+hismaxlzy);
    tree[k].hismaxlzy=max(tree[k].hismaxlzy,tree[k].maxlzy+hismaxlzy);
    tree[k].maxval+=maxlzy,tree[k].maxlzy+=maxlzy;
    tree[k].hislzy=max(tree[k].hislzy,tree[k].lzy+hislzy);
    if(tree[k].sndmax!=-INF) tree[k].sndmax+=lzy;
    tree[k].lzy+=lzy;
}
void pushdown(int k){
    int maxx=max(tree[k<<1].maxval,tree[k<<1|1].maxval);
    update(k<<1,tree[k].lzy,tree[k].hislzy,
           tree[k<<1].maxval==maxx?tree[k].maxlzy:tree[k].lzy,
           tree[k<<1].maxval==maxx?tree[k].hismaxlzy:tree[k].hislzy);
    update(k<<1|1,tree[k].lzy,tree[k].hislzy,
           tree[k<<1|1].maxval==maxx?tree[k].maxlzy:tree[k].lzy,
           tree[k<<1|1].maxval==maxx?tree[k].hismaxlzy:tree[k].hislzy);
    tree[k].lzy=tree[k].maxlzy=tree[k].hislzy=tree[k].hismaxlzy=0;
}
void build(int k,int l,int r){
    tree[k].l=l,tree[k].r=r;
    tree[k].lzy=tree[k].maxlzy=tree[k].hislzy=tree[k].hismaxlzy=0;
    if(l==r){
        tree[k].val=tree[k].maxval=tree[k].hismax=a[l];
        tree[k].sndmax=-INF,tree[k].maxcnt=1;
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void modify_sum(int k,int l,int r,int val){
    if(tree[k].l>=l&&tree[k].r<=r){
        update(k,val,val,val,val);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_sum(k<<1,l,r,val);
    if(r>mid) modify_sum(k<<1|1,l,r,val);
    pushup(k);
}
void modify_min(int k,int l,int r,int val){
    if(tree[k].maxval<=val) return;
    if(tree[k].l>=l&&tree[k].r<=r&&val>tree[k].sndmax){
        update(k,0,0,val-tree[k].maxval,val-tree[k].maxval);
        return;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid) modify_min(k<<1,l,r,val);
    if(r>mid) modify_min(k<<1|1,l,r,val);
    pushup(k);
}
int query_sum(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].val;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=0;
    if(l<=mid) res+=query_sum(k<<1,l,r);
    if(r>mid) res+=query_sum(k<<1|1,l,r);
    return res;
}
int query_max(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].maxval;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_max(k<<1,l,r));
    if(r>mid) res=max(res,query_max(k<<1|1,l,r));
    return res;
}
int query_hismax(int k,int l,int r){
    if(tree[k].l>=l&&tree[k].r<=r){
        return tree[k].hismax;
    }
    pushdown(k);
    int mid=(tree[k].l+tree[k].r)>>1,res=-INF;
    if(l<=mid) res=max(res,query_hismax(k<<1,l,r));
    if(r>mid) res=max(res,query_hismax(k<<1|1,l,r));
    return res;
}
signed main(){
//    freopen("test.in","r",stdin);
//    freopen("cpp.out","w",stdout);
    n=read(),q=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    while(q--){
        int opt=read();
        if(opt==1){
            int l=read(),r=read(),val=read();
            modify_sum(1,l,r,val);
        }
        if(opt==2){
            int l=read(),r=read(),val=read();
            modify_min(1,l,r,val);
        }
        if(opt==3){
            int l=read(),r=read();
            printf("%lld\n",query_sum(1,l,r));
        }
        if(opt==4){
            int l=read(),r=read();
            printf("%lld\n",query_max(1,l,r));
        }
        if(opt==5){
            int l=read(),r=read();
            printf("%lld\n",query_hismax(1,l,r));
        }
    }
    return 0;
}
posted @   冬天的雨WR  阅读(77)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
Live2D
点击右上角即可分享
微信分享提示