线段树的区间最值操作与区间历史最值

前提知识

基本的线段树写法:
洛谷线段树1
洛谷线段树2

以下是正文

一、区间最值操作

在这里插入图片描述

例题一、

Gorgeous Sequence

本题要求我们实现三个操作
在这里插入图片描述

做法一、

对于第二种和第三种操作我们是已经知道如何写了。

问题就在于第一种操作。

考虑第一种操作是取min
因此当我们这个区间内的最大值 
1.如果比给定的k来得小 那么我们不需要任何操作
2.如果这个k<最大值 但是k > 第二大的值(以下称为次大值)
那么我们只需要将最大值全部变成k,修改一下区间和即可.
3.如果这个k<最大值 && k < 次大值 
那么我们当前这个结点是处理不了的,直接push_down交给儿子结点
Code
#include <iostream>
#include <cstring>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
typedef long long ll;
const int N = 1100000;
struct Node{
    int L,R,tag;//这个tag是维护区间最值操作的
    ll sum;
    int fir_big,sec_big;
    int fir_num;
}tree[N*4];
int a[N];
inline void push_up(int);
void build_tree(int l,int r,int p){
    //tag的取值是0~2^31-1 因此我们取-1
    tree[p].L=l,tree[p].R=r,tree[p].tag=-1;
    if(l==r){
        tree[p].sum=a[l];
        tree[p].fir_big=a[l];
        tree[p].sec_big=-1;
        tree[p].fir_num=1;
        return;
    }
    int mid = (l+r)>>1;
    build_tree(l,mid,lc);
    build_tree(mid+1,r,rc);
    push_up(p);
}
inline void push_up(int p){
    tree[p].sum=tree[lc].sum+tree[rc].sum;
    if(tree[lc].fir_big==tree[rc].fir_big){
        tree[p].fir_big=tree[lc].fir_big;
        tree[p].fir_num=tree[lc].fir_num+tree[rc].fir_num;
        tree[p].sec_big=max(tree[lc].sec_big,tree[rc].sec_big);
    }else if(tree[lc].fir_big>tree[rc].fir_big){
        tree[p].fir_big=tree[lc].fir_big;
        tree[p].fir_num=tree[lc].fir_num;
        tree[p].sec_big=max(tree[lc].sec_big,tree[rc].fir_big);
    }else{
        tree[p].fir_big=tree[rc].fir_big;
        tree[p].fir_num=tree[rc].fir_num;
        tree[p].sec_big=max(tree[rc].sec_big,tree[lc].fir_big);
    }
}
inline void update(int p,int cnt_val){
    if(cnt_val<tree[p].fir_big){
        //最大值全部变成cnt_val
        tree[p].sum+=(1ll*cnt_val-tree[p].fir_big)*tree[p].fir_num;
        tree[p].fir_big=tree[p].tag=cnt_val;
    }
}
inline void push_down(int p){
    if(~tree[p].tag){
        update(lc,tree[p].tag);
        update(rc,tree[p].tag);
        tree[p].tag=-1;
    }
}
void modify(int p,int l,int r,int cnt_val){
    if(tree[p].L>r||tree[p].R<l||tree[p].fir_big<=cnt_val) return;
    if(tree[p].L>=l&&tree[p].R<=r&&tree[p].sec_big<cnt_val){
        update(p,cnt_val);
        return;
    }
    push_down(p);
    modify(lc,l,r,cnt_val);
    modify(rc,l,r,cnt_val);
    push_up(p);
}
int queryMax(int p,int l,int r){
    if(tree[p].L>r||tree[p].R<l) return 0;
    if(tree[p].L>=l&&tree[p].R<=r){
        return tree[p].fir_big;
    }
    push_down(p);
    return max(queryMax(lc,l,r),queryMax(rc,l,r));
}
ll querySum(int p,int l,int r){
    if(tree[p].L>r||tree[p].R<l) return 0;
    if(tree[p].L>=l&&tree[p].R<=r){
        return tree[p].sum;
    }
    push_down(p);
    return querySum(lc,l,r)+querySum(rc,l,r);
}
#undef lc
#undef rc
int read(){
	int x=0; char ch=0;
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
	return x;
}
#if 0
这份代码完成三个功能
1.区间最小值操作
2.求区间最大值
3.求区间和
#endif // 0
int main()
{
    int t,n,m;
    t=read();
    while(t--){
        n=read();
        m=read();
        for(int i=1;i<=n;i++){
           a[i]=read();
        }
        build_tree(1,n,1);
        for(int i=1;i<=m;i++){
            int op,x,y,t;
            op=read();
            if(op==0){
                x=read();
                y=read();
                t=read();
                modify(1,x,y,t);
            }else if(op==1){
                x=read();
                y=read();
                printf("%d\n",queryMax(1,x,y));
            }else{
                x=read();
                y=read();
                printf("%lld\n",querySum(1,x,y));
            }
        }
    }
    return 0;
}


做法二、

2021.9.8复习更新另一个代码;
这份代码和上面那种做法不同点在于,他不是维护一个改变的tag,而是维护一个加法的tag;

按照例题二的思路,我们将值分为三种,最大值、次大值、其他值;

维护两个标记,最大值的加法标记,其他值的加法标记;

#include <iostream>
#include <cstring>

#define lc (p<<1)
#define rc (p<<1|1)

using namespace std;

typedef long long ll;

const int N = 1000010;

struct Node{
    int l,r,mx,sec_mx,mx_num;
    int mx_tag,other_tag;
    ll sum;
}tr[N<<2];

int a[N],t,n,m;
void push_down(int p);
int read()
{
    int x=0; char ch=0;
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
    return x;
}
void push_up(int p){
    tr[p].sum = tr[lc].sum + tr[rc].sum;
    if(tr[lc].mx == tr[rc].mx){
        tr[p].mx = tr[lc].mx;
        tr[p].mx_num = tr[lc].mx_num + tr[rc].mx_num;
        tr[p].sec_mx = max(tr[lc].sec_mx,tr[rc].sec_mx);
    }else if(tr[lc].mx > tr[rc].mx){
        tr[p].mx = tr[lc].mx;
        tr[p].mx_num = tr[lc].mx_num;
        tr[p].sec_mx = max(tr[lc].sec_mx,tr[rc].mx);
    }else{
        tr[p].mx = tr[rc].mx;
        tr[p].mx_num = tr[rc].mx_num;
        tr[p].sec_mx = max(tr[rc].sec_mx,tr[lc].mx);
    }
}
void build(int p,int l,int r){
    tr[p].l = l,tr[p].r = r;
    tr[p].sec_mx = -1;//因为数据范围是从0开始的
    tr[p].mx_tag = tr[p].other_tag = 0;
    if(l == r){
        tr[p].sum = tr[p].mx = a[l];
        tr[p].mx_num = 1;
        return;
    }
    int mid = (l+r) >> 1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(p);
}
//给这个节点的最大值、其他值加上某些数
void node_update_fun(int p,int mxk,int otherk){
    tr[p].sum += 1ll*mxk*tr[p].mx_num + 1ll*(tr[p].r-tr[p].l+1-tr[p].mx_num)*otherk;
    if(tr[p].sec_mx != -1) tr[p].sec_mx += otherk;
    tr[p].mx  += mxk;
    tr[p].mx_tag += mxk;
    tr[p].other_tag += otherk;
}
void range_min_fun(int p,int l,int r,int k){
    if(k>=tr[p].mx) return;
    if(tr[p].l>=l&&tr[p].r<=r&&tr[p].sec_mx < k){
        node_update_fun(p,k-tr[p].mx,0);
        return;
    }
    push_down(p);
    int mid = (tr[p].l + tr[p].r) >> 1;
    if(r<=mid){
        range_min_fun(lc,l,r,k);
    }else if(l>mid){
        range_min_fun(rc,l,r,k);
    }else{
        range_min_fun(lc,l,mid,k);
        range_min_fun(rc,mid+1,r,k);
    }
    push_up(p);
}
void push_down(int p){
    int mxk = tr[p].mx_tag;
    int otherk = tr[p].other_tag;
    int mx = max(tr[lc].mx,tr[rc].mx);
    //分清楚左右儿子的值到底应该被哪个k所加
    node_update_fun(lc,tr[lc].mx==mx?mxk:otherk,otherk);
    node_update_fun(rc,tr[rc].mx==mx?mxk:otherk,otherk);
    tr[p].mx_tag = tr[p].other_tag = 0;
}
ll query_sum(int p,int l,int r){
    if(tr[p].l>=l&&tr[p].r<=r){
        return tr[p].sum;
    }
    push_down(p);
    int mid = (tr[p].l+tr[p].r)>>1;
    if(r<=mid){
        return query_sum(lc,l,r);
    }else if(l>mid){
        return query_sum(rc,l,r);
    }else{
        return query_sum(lc,l,mid) +
        query_sum(rc,mid+1,r);
    }
}
int query_max(int p,int l,int r){
    if(tr[p].l>=l&&tr[p].r<=r){
        return tr[p].mx;
    }
    push_down(p);
    int mid = (tr[p].l+tr[p].r)>>1;
    if(r<=mid){
        return query_max(lc,l,r);
    }else if(l>mid){
        return query_max(rc,l,r);
    }else{
        return max(query_max(lc,l,mid),
                   query_max(rc,mid+1,r));
    }

}
int main()
{
    t=read();
    while(t--){
        n=read();
        m=read();
        for(int i=1;i<=n;++i) a[i] =read();
        build(1,1,n);
        int op,l,r,k;
        while(m--){
            op=read();
            l=read();
            r=read();
            if(op == 0){
                k=read();
                range_min_fun(1,l,r,k);
            }else if(op == 1){
                 printf("%d\n",query_max(1,l,r));
            }else{
                printf("%lld\n",query_sum(1,l,r));
            }
        }
    }
    return 0;
}

例题二、

BZOJ4695
这题在上一题的基础之上

  • 增加了区间加
  • 区间最大值操作
  • 求区间最小值

根据上一题区间最小值的操作 我们来仿造区间最大值操作

对于区间最小值操作
我们维护:区间最大值及个数、次大值
那么对于区间最大值操作
我们维护:区间最小值及个数、次小值

那么我们就将数分为了三类:最大值、最小值、其他值
对于题目要求的三种修改操作

  • 对于区间加(减),我们直接对这三类数加(减) 即可
  • 对于区间最小值操作以及最大值操作
    思路与例题一处相同。
    此处以区间最大值操作为例
    1.如果最大值比给定的k来得小 那么我们不需要任何操作
    2.如果这个k<最大值 但是k > 次大值
    那么我们只需要将最大值全部变成k,修改一下区间和即可.
    3.如果这个k<最大值 && k < 次大值
    那么我们当前这个结点是处理不了的,直接push_down交给儿子结点

那么,我们需要维护三个tag

  • 最大值tag
  • 最小值tag
  • 其他值tag

但是这里有个问题,如果一个区间内的数字很少。

	比如只有1个或者2个
	当只有一个数字的时候 那么最大值是等于最小值的
	我们在push_down的时候不要搞错标记
	
	亦或者只有两个数字的时候 有可能发生次大值等于最小值
	也有可能发生次小值等于最大值 
	此时也要注意是哪个标记的作用

还有一个问题,当前节点的最大最小值是从儿子节点取过来的

此处以最大值为例
如果儿子节点不包含最大值 
那么我们给它加的标记应该是其他值的标记(代码中的t3)
如果包含了 那么应该加最大值的标记(代码中的t2)
Code
#include <iostream>
#include <cstdio>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
typedef long long ll;
const int N = 550000;
const int INF = 1e9;
struct Node{
    int L,R;
    ll sum;
    //分别是最小值 最大值 其他值的tag
    int tag1,tag2,tag3;
    //最大值 最小值 次大值 次小值 最大值个数 最小值个数
    int fir_max,fir_min,sec_max,sec_min,mx_num,mn_num;
}tree[N<<2];
int a[N];
void push_up(int p){
    tree[p].sum=tree[lc].sum+tree[rc].sum;
    if(tree[lc].fir_min==tree[rc].fir_min){
        tree[p].fir_min=tree[lc].fir_min;
        tree[p].mn_num=tree[lc].mn_num+tree[rc].mn_num;
        tree[p].sec_min=min(tree[lc].sec_min,tree[rc].sec_min);
    }else if(tree[lc].fir_min<tree[rc].fir_min){
        tree[p].fir_min=tree[lc].fir_min;
        tree[p].mn_num=tree[lc].mn_num;
        tree[p].sec_min=min(tree[lc].sec_min,tree[rc].fir_min);
    }else{
        tree[p].fir_min=tree[rc].fir_min;
        tree[p].mn_num=tree[rc].mn_num;
        tree[p].sec_min=min(tree[rc].sec_min,tree[lc].fir_min);
    }
    if(tree[lc].fir_max==tree[rc].fir_max){
        tree[p].fir_max=tree[lc].fir_max;
        tree[p].mx_num=tree[lc].mx_num+tree[rc].mx_num;
        tree[p].sec_max=max(tree[lc].sec_max,tree[rc].sec_max);
    }else if(tree[lc].fir_max>tree[rc].fir_max){
        tree[p].fir_max=tree[lc].fir_max;
        tree[p].mx_num=tree[lc].mx_num;
        tree[p].sec_max=max(tree[lc].sec_max,tree[rc].fir_max);
    }else{
        tree[p].fir_max=tree[rc].fir_max;
        tree[p].mx_num=tree[rc].mx_num;
        tree[p].sec_max=max(tree[rc].sec_max,tree[lc].fir_max);
    }
}
void build(int p,int l,int r){
    tree[p].L=l,tree[p].R=r;
    tree[p].tag1=tree[p].tag2=tree[p].tag3=0;
    if(l==r){
        tree[p].fir_max=tree[p].fir_min=tree[p].sum=a[l];
        tree[p].sec_max=-INF;
        tree[p].sec_min=INF;
        tree[p].mx_num=tree[p].mn_num=1;
        return;
    }
    int mid = (l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(p);
}
//t1,t2,t3分别是最小值 最大值 其他值的tag标记
void update(int p,int t1,int t2,int t3){
    //处理sum
    if(tree[p].fir_max==tree[p].fir_min){
        //如果只有一个值(最大值等于最小值)
        //那么将其他值的tag丢掉
        if(t1==t3) t1=t2;
        else if(t2==t3) t2=t1;
        tree[p].sum+=1ll*tree[p].mx_num*t2;
        //else t2=t1;
        //tree[p].sum+=1ll*t1*tree[p].mn_num;
    }
    else{
        tree[p].sum+=1ll*tree[p].mx_num*t2+1ll*tree[p].mn_num*t1+
        (tree[p].R-tree[p].L+1-tree[p].mx_num-tree[p].mn_num)*t3*1ll;
    }
    //处理次大值
    //如果次大值和最小值相等 那么给它加tag1的值
    //否则加tag3的值
    //处理次小值同理
    if(tree[p].sec_max==tree[p].fir_min){
        tree[p].sec_max+=t1;
    }else if(tree[p].sec_max!=-INF){
        tree[p].sec_max+=t3;
    }
    //处理次小值
    if(tree[p].sec_min==tree[p].fir_max){
        tree[p].sec_min+=t2;
    }else if(tree[p].sec_min!=INF){
        tree[p].sec_min+=t3;
    }

    //处理最大值、最小值、以及tag
    tree[p].fir_max+=t2;
    tree[p].fir_min+=t1;
    tree[p].tag1+=t1;
    tree[p].tag2+=t2;
    tree[p].tag3+=t3;
}
void push_down(int p){
    int mx = max(tree[lc].fir_max,tree[rc].fir_max);
    int mn = min(tree[lc].fir_min,tree[rc].fir_min);
    int l1,l2,r1,r2;
    l1=tree[lc].fir_min==mn?tree[p].tag1:tree[p].tag3;
    l2=tree[lc].fir_max==mx?tree[p].tag2:tree[p].tag3;
    update(lc,l1,l2,tree[p].tag3);
    r1=tree[rc].fir_min==mn?tree[p].tag1:tree[p].tag3;
    r2=tree[rc].fir_max==mx?tree[p].tag2:tree[p].tag3;
    update(rc,r1,r2,tree[p].tag3);
    tree[p].tag1=tree[p].tag2=tree[p].tag3=0;
}

//给一个区间[L,R] 加上一个数x
void fun1(int p,int l,int r,int k){
    if(tree[p].L>r||tree[p].R<l) return;
    if(tree[p].L>=l&&tree[p].R<=r){
        update(p,k,k,k);
        return;
    }
    push_down(p);
    fun1(lc,l,r,k);
    fun1(rc,l,r,k);
    push_up(p);
}
//把一个区间[L,R] 里小于x 的数变成x
void fun2(int p,int l,int r,int k){
     if(tree[p].L>r||tree[p].R<l||tree[p].fir_min>=k) return;
     if(tree[p].L>=l&&tree[p].R<=r&&tree[p].sec_min>k){
        update(p,k-tree[p].fir_min,0,0);
        return;
     }
    push_down(p);
    fun2(lc,l,r,k);
    fun2(rc,l,r,k);
    push_up(p);
}
//把一个区间[L,R] 里大于x 的数变成x
void fun3(int p,int l,int r,int k){
    if(tree[p].L>r||tree[p].R<l||tree[p].fir_max<=k) return;
    if(tree[p].L>=l&&tree[p].R<=r&&tree[p].sec_max<k){
        update(p,0,k-tree[p].fir_max,0);
        return;
     }
    push_down(p);
    fun3(lc,l,r,k);
    fun3(rc,l,r,k);
    push_up(p);
}
//求区间[L,R] 的和
ll fun4(int p,int l,int r){
     if(tree[p].L>r||tree[p].R<l) return 0;
     if(tree[p].L>=l&&tree[p].R<=r){
        return tree[p].sum;
     }
     push_down(p);
     return fun4(lc,l,r)+fun4(rc,l,r);
}
//求区间[L,R] 的最大值
int fun5(int p,int l,int r){
    if(tree[p].L>r||tree[p].R<l) return -INF;
    if(tree[p].L>=l&&tree[p].R<=r){
        return tree[p].fir_max;
     }
      push_down(p);
     return max(fun5(lc,l,r),fun5(rc,l,r));
}
//求区间[L,R] 的最小值
int fun6(int p,int l,int r){
    if(tree[p].L>r||tree[p].R<l) return INF;
    if(tree[p].L>=l&&tree[p].R<=r){
        return tree[p].fir_min;
     }
      push_down(p);
     return min(fun6(lc,l,r),fun6(rc,l,r));
}
#undef lc
#undef rc
int read()
{
	int x=0, w=0; char ch=0;
	while (!isdigit(ch)) w|=ch=='-', ch=getchar();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
	return w?-x:x;
}
template<class T>
inline void write(T x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');//取出个位
}

int main()
{
    int n;
    n=read();
    for(int i=1;i<=n;i++){
       a[i]=read();
    }
    build(1,1,n);
    int m;
    m=read();
    while(m--){
        int op,l,r,k;
        op=read();
        switch(op){
            case 1:
                l=read();r=read();k=read();
                fun1(1,l,r,k);
                break;
            case 2:
                l=read();r=read();k=read();
                fun2(1,l,r,k);
                break;
            case 3:
                l=read();r=read();k=read();
                fun3(1,l,r,k);
                break;
            case 4:
                l=read();r=read();
                write(fun4(1,l,r));
                putchar('\n');
                break;
            case 5:
                 l=read();r=read();
                 write(fun5(1,l,r));
                 putchar('\n');
                break;
            case 6:
                 l=read();r=read();
                 write(fun6(1,l,r));
                 putchar('\n');
                break;
        }
    }
    return 0;
}

代码二

2021.9.7复习,写了一份新的,我觉得更加适合理解,思路和上面是一致的;

#include <iostream>

using namespace std;

typedef long long ll;

const int N = 5e5+10;

const int INF = 1e9;

#define lc (p<<1)
#define rc (p<<1|1)

struct Node{
    int l,r,mx,mn,mx_num,mn_num,sec_mx,sec_mn;
    ll sum;
    int mx_tag,mn_tag,other_tag;
}tr[N<<2];
int n,m,a[N];
void push_down(int p);

void push_up(int p){
    Node &left = tr[lc],&right = tr[rc],&root = tr[p];
    root.sum = left.sum + right.sum;
    //操作最大值、次大值
    if(left.mx == right.mx){
        root.mx = left.mx;
        root.mx_num = left.mx_num + right.mx_num;
        root.sec_mx = max(left.sec_mx,right.sec_mx);
    }else if(left.mx > right.mx){
        root.mx = left.mx;
        root.mx_num = left.mx_num;
        root.sec_mx = max(left.sec_mx,right.mx);
    }else{
        root.mx = right.mx;
        root.mx_num = right.mx_num;
        root.sec_mx = max(right.sec_mx,left.mx);
    }
    //最小值、次小值
    if(left.mn == right.mn){
        root.mn = left.mn;
        root.mn_num = left.mn_num + right.mn_num;
        root.sec_mn = min(left.sec_mn,right.sec_mn);
    }else if(left.mn < right.mn){
        root.mn = left.mn;
        root.mn_num = left.mn_num;
        root.sec_mn = min(left.sec_mn,right.mn);
    }else{
        root.mn = right.mn;
        root.mn_num = right.mn_num;
        root.sec_mn = min(right.sec_mn,left.mn);
    }
}
//这个方法对某个节点的最大值、最小值、其他值加上某些数
void node_update_fun(int p,int mnk,int mxk,int otherk){
    Node & root = tr[p];
    if(root.mx == root.mn){
        //不应该被其他值的标签所影响,因为这里只有一个值
        if(mnk == otherk) mnk = mxk;
        else mxk = mnk;
        root.sum += 1ll*root.mn_num * mnk;
    }else{
        root.sum += 1ll*root.mn_num * mnk + 1ll*root.mx_num * mxk
        + 1ll*(root.r-root.l+1-root.mn_num-root.mx_num)*otherk;
    }
    //操作次值
    if(root.mx == root.sec_mn){
        root.sec_mn += mxk;
    }else if(root.sec_mn != INF){
        root.sec_mn += otherk;
    }
    if(root.mn == root.sec_mx){
        root.sec_mx += mnk;
    }else if(root.sec_mx != -INF){
        root.sec_mx +=otherk;
    }
    root.mx += mxk;
    root.mn += mnk;

    root.mx_tag += mxk;
    root.mn_tag += mnk;
    root.other_tag += otherk;
}
void range_max_fun(int p,int l,int r,int k){
    Node &root = tr[p];
    if(root.mn >= k) return;
    if(root.l>=l&&root.r<=r&&root.sec_mn > k){
        node_update_fun(p,k-root.mn,0,0);
        return;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        range_max_fun(lc,l,r,k);
    }else if(l>mid){
        range_max_fun(rc,l,r,k);
    }else{
        range_max_fun(lc,l,mid,k);
        range_max_fun(rc,mid+1,r,k);
    }
    push_up(p);
}
void range_min_fun(int p,int l,int r,int k){
    Node &root = tr[p];
    if(root.mx <= k) return;
    if(root.l>=l&&root.r<=r&&root.sec_mx<k){
        node_update_fun(p,0,k-root.mx,0);
        return;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        range_min_fun(lc,l,r,k);
    }else if(l>mid){
        range_min_fun(rc,l,r,k);
    }else{
        range_min_fun(lc,l,mid,k);
        range_min_fun(rc,mid+1,r,k);
    }
    push_up(p);
}
void range_add_fun(int p,int l,int r,int k){
    Node & root = tr[p];
    if(root.l>=l&&root.r<=r){
        node_update_fun(p,k,k,k);
        return;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        range_add_fun(lc,l,r,k);
    }else if(l>mid){
        range_add_fun(rc,l,r,k);
    }else{
        range_add_fun(lc,l,mid,k);
        range_add_fun(rc,mid+1,r,k);
    }
    push_up(p);
}
ll query_sum(int p,int l,int r){
    Node & root = tr[p];
    if(root.l>=l&&root.r<=r){
        return root.sum;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        return query_sum(lc,l,r);
    }else if(l>mid){
        return query_sum(rc,l,r);
    }else{
        return query_sum(lc,l,mid)+
        query_sum(rc,mid+1,r);
    }
}
int query_max(int p,int l,int r){
    Node & root = tr[p];
    if(root.l>=l&&root.r<=r){
        return root.mx;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        return query_max(lc,l,r);
    }else if(l>mid){
        return query_max(rc,l,r);
    }else{
        return max(query_max(lc,l,mid),
        query_max(rc,mid+1,r));
    }
}
int query_min(int p,int l,int r){
    Node & root = tr[p];
    if(root.l>=l&&root.r<=r){
        return root.mn;
    }
    push_down(p);
    int mid = (root.l + root.r) >> 1;
    if(r<=mid){
        return query_min(lc,l,r);
    }else if(l>mid){
        return query_min(rc,l,r);
    }else{
        return min(query_min(lc,l,mid),
        query_min(rc,mid+1,r));
    }
}
void push_down(int p){
    int mx = max(tr[lc].mx,tr[rc].mx);
    int mn = min(tr[lc].mn,tr[rc].mn);
    int mxk = tr[p].mx_tag,mnk = tr[p].mn_tag,otherk=tr[p].other_tag;
    node_update_fun(lc,tr[lc].mn==mn?mnk:otherk,
                    tr[lc].mx==mx?mxk:otherk,
                    otherk
                    );
    node_update_fun(rc,tr[rc].mn==mn?mnk:otherk,
                    tr[rc].mx==mx?mxk:otherk,
                    otherk
                    );
    tr[p].mx_tag = tr[p].mn_tag = tr[p].other_tag = 0;
}

void build(int p,int l,int r){
    tr[p].l = l,tr[p].r = r;
    tr[p].mn_tag = tr[p].mx_tag = tr[p].other_tag = 0;
    tr[p].mx = tr[p].sec_mx = -INF;
    tr[p].mn = tr[p].sec_mn = INF;
    if(l==r){
        tr[p].sum = tr[p].mn = tr[p].mx = a[l];
        tr[p].mx_num = tr[p].mn_num = 1;
        return;
    }
    int mid = (l+r) >> 1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(p);
}

int main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin >> n;
    for(int i=1;i<=n;++i) cin >> a[i];
    build(1,1,n);
    cin >> m;
    int op,l,r,k;
    while(m--){
        cin >> op >> l >> r;
        switch(op){
            case 1:
                cin >> k;
                range_add_fun(1,l,r,k);
                break;
            case 2:
                cin >> k;
                range_max_fun(1,l,r,k);
                break;
            case 3:
                cin >> k;
                range_min_fun(1,l,r,k);
                break;
            case 4:
                cout << query_sum(1,l,r) <<'\n';
                break;
            case 5:
                cout << query_max(1,l,r) << '\n';
                break;
            case 6:
                cout << query_min(1,l,r) << '\n';
                break;
        }
    }
    return 0;
}

例题三、

待完成…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个用线段区间的示例代码,以查询区间最小为例: ```cpp #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 100005; int n, m; int a[MAXN]; struct Node { int l, r, minn; } tr[MAXN*4]; // 建立线段 void build(int u, int l, int r) { tr[u].l = l, tr[u].r = r; if (l == r) { // 叶子节点 tr[u].minn = a[l]; return; } int mid = (l + r) >> 1; build(u<<1, l, mid); build(u<<1|1, mid+1, r); tr[u].minn = min(tr[u<<1].minn, tr[u<<1|1].minn); } // 查询区间最小 int query(int u, int ql, int qr) { if (ql <= tr[u].l && tr[u].r <= qr) { // 完全包含 return tr[u].minn; } int mid = (tr[u].l + tr[u].r) >> 1; int res = 0x3f3f3f3f; // 初始化为一个极大 if (ql <= mid) res = min(res, query(u<<1, ql, qr)); // 左子区间有交集 if (qr > mid) res = min(res, query(u<<1|1, ql, qr)); // 右子区间有交集 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 l, r; scanf("%d%d", &l, &r); printf("%d\n", query(1, l, r)); } return 0; } ``` 这段代码中,建立线段的过程采用递归方式实现,查询区间最小的过程也采用递归方式实现。对于每个节点,维护该节点对应区间的最小。查询区间最小的过程,采用线段的递归查询方式,将查询区间划分成若干个离散的单元,并依次查询每个单元,最终得到查询区间的最小

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值