跳转至

吉司机线段树

详解

www.luogu.com.cn

多个例题

www.luogu.com.cn

题型:区间最值笼统地指求区间的最值以及区间所有数对 x 取最值(即令 \(a_i​=\max/\min(a_i​,x)\))这一类的查询与修改操作。

例题 #1 HDU5306 Gorgeous Sequence

维护一个序列 a:

  • 0 l r t \(\forall l\le i\le r,\ a_i=\min(a_i,t)\)

  • 1 l r 输出区间 [l,r] 中的最大值。

  • 2 l r 输出区间和。

  • 多组数据,\(T\le 100,n\le 10^6,\sum m\le 10^6\)

引用自 OI wiki

区间取 min,意味着只对那些大于 t 的数有更改。因此这个操作的对象不再是整个区间,而是「这个区间中大于 t 的数」。于是我们可以有这样的思路:

每个结点维护该区间的最大值 Mx1、次大值 Mx2、区间和 Sum 以及最大值的个数 Cnt。接下来我们考虑区间对 t 取 \min 的操作。 如果 \(Mx1\le t\),显然这个 t 是没有意义的,直接返回; 如果 \(Mx2<t\le Mx1\),那么这个 t 就能更新当前区间中的最大值。于是我们让区间和加上 Cnt(t-Mx1),然后更新 Mx1 为 t,并打一个标记。 如果 \(t\le Mx2\),那么这时你发现你不知道有多少个数涉及到更新的问题。于是我们的策略就是,暴力递归向下操作。然后上传信息。

例题 #2 线段树 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\) 的最大值。

在每一次操作后,我们都进行一次更新,让 \(B_i\gets\max(B_i,A_i)\)

  • 对于全部测试数据,保证 \(1\leq n,m\leq 5\times 10^5\)\(-5\times10^8\leq A_i\leq 5\times10^8\)\(op\in[1,5]\)\(1 \leq l\leq r \leq n\)\(-2000\leq k\leq 2000\)\(-5\times10^8\leq v\leq 5\times10^8\)

提示

本题输入量较大,请使用合理高效的读入方法。

代码分析

数据

struct node{
    int mx1a,cnt,mx2a,sum,mx1b;//a的最大值,a的最大值的个数,a的次大值,区间和,b的最大值
    int tg1,tg2,tg3,tg4;//4个tag分别是对应mx1a,mx2a,tg1的max,t2的max
}t[N<<2];

难点:tag对值的更新

void tagModify(int t1,int t2,int t3,int t4,int x,int l,int r){
    t[x].sum+=t1*t[x].cnt+t2*(r-l+1-t[x].cnt);
    t[x].mx1b=max(t[x].mx1b,t[x].mx1a+t3);
    t[x].mx1a+=t1;

    if(t[x].mx2a!=-INF)t[x].mx2a+=t2;


    t[x].tg3=max(t[x].tg1+t3,t[x].tg3);
    t[x].tg4=max(t[x].tg2+t4,t[x].tg4);
    t[x].tg1+=t1;t[x].tg2+=t2;
}

难点:两种操作

  • 区间加操作

线段树框架就省略了,我们只看当找到了要修改的节点时对节点的修改

void tagAdd(int x,int v,int l,int r){
    t[x].sum+=v*t[x].cnt+v*(r-l+1-t[x].cnt);
    t[x].mx1a+=v;
    t[x].mx1b=max(t[x].mx1b,t[x].mx1a);
    if(t[x].mx2a!=-INF)t[x].mx2a+=v;

    t[x].tg1+=v;t[x].tg2+=v;
    t[x].tg3=max(t[x].tg1,t[x].tg3);
    t[x].tg4=max(t[x].tg2,t[x].tg4);
}
  • 区间min修改操作

注意因为吉司机的特殊性,所以我们需要多一些约束条件。

void changeMin(int x,int l,int r,int pl,int pr,int v){
    if(pl>r||pr<l||v>=t[x].mx1a)return ;//注意多了条件!!!
    if(l>=pl&&r<=pr&&t[x].mx2a<v){//注意多了条件!!!
        tagMin(x,v);
        return ;
    }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    changeMin(x<<1,l,mid,pl,pr,v);
    changeMin(x<<1|1,mid+1,r,pl,pr,v);
    pushup(x);
}

难度:pushdown标记下传操作

void pushdown(int x,int l,int r){
    int mid=l+r>>1;

    int mx=max(t[x<<1].mx1a,t[x<<1|1].mx1a);
    if(t[x<<1].mx1a==mx)tagModify(t[x].tg1,t[x].tg2,t[x].tg3,t[x].tg4,x<<1,l,mid);
    else tagModify(t[x].tg2,t[x].tg2,t[x].tg4,t[x].tg4,x<<1,l,mid);

    if(t[x<<1|1].mx1a==mx)tagModify(t[x].tg1,t[x].tg2,t[x].tg3,t[x].tg4,x<<1|1,mid+1,r);
    else tagModify(t[x].tg2,t[x].tg2,t[x].tg4,t[x].tg4,x<<1|1,mid+1,r);

    //清空tag
    t[x].tg1=t[x].tg2=t[x].tg3=t[x].tg4=0;
}

Code AC

/*
CB Ntsc
*/

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair

const int N = 1e6 + 5;
const int M = 16;
const int INF = 2e9 + 5;
const int MOD = 9999973;


#define rd read()
inline int read() {
    int xx = 0, ff = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            ff = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') xx = xx * 10 + (ch - '0'), ch = getchar();
    return xx * ff;
}
inline void write(int out) {
    if (out < 0)
        putchar('-'), out = -out;
    if (out > 9)
        write(out / 10);
    putchar(out % 10 + '0');
}

bool f1;
int tr[N<<2],a[N],pos[N];
int q, n, m, ans, T;
bool f2;

struct node{
    int mx1a,cnt,mx2a,sum,mx1b;
    int tg1,tg2,tg3,tg4;//4个tag分别是对应mx1a,mx2a
}t[N<<2];


//标记传递

void pushup(int x){
    //维护最大值,区间和
    t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
    t[x].mx1a=max(t[x<<1].mx1a,t[x<<1|1].mx1a);
    t[x].mx1b=max(t[x<<1].mx1b,t[x<<1|1].mx1b);
    //维护次大值及最大值的个数
    if(t[x<<1].mx1a==t[x<<1|1].mx1a){
        t[x].cnt=t[x<<1].cnt+t[x<<1|1].cnt;
        t[x].mx2a=max(t[x<<1].mx2a,t[x<<1|1].mx2a);
    }else if(t[x<<1].mx1a>t[x<<1|1].mx1a){
        t[x].cnt=t[x<<1].cnt;
        t[x].mx2a=max(t[x<<1].mx2a,t[x<<1|1].mx1a);
    }else{
        t[x].cnt=t[x<<1|1].cnt;
        t[x].mx2a=max(t[x<<1].mx1a,t[x<<1|1].mx2a);
    }
}

void tagModify(int t1,int t2,int t3,int t4,int x,int l,int r){
    t[x].sum+=t1*t[x].cnt+t2*(r-l+1-t[x].cnt);
    t[x].mx1b=max(t[x].mx1b,t[x].mx1a+t3);
    t[x].mx1a+=t1;

    if(t[x].mx2a!=-INF)t[x].mx2a+=t2;


    t[x].tg3=max(t[x].tg1+t3,t[x].tg3);
    t[x].tg4=max(t[x].tg2+t4,t[x].tg4);
    t[x].tg1+=t1;t[x].tg2+=t2;
}


void pushdown(int x,int l,int r){
    int mid=l+r>>1;

    int mx=max(t[x<<1].mx1a,t[x<<1|1].mx1a);
    if(t[x<<1].mx1a==mx)tagModify(t[x].tg1,t[x].tg2,t[x].tg3,t[x].tg4,x<<1,l,mid);
    else tagModify(t[x].tg2,t[x].tg2,t[x].tg4,t[x].tg4,x<<1,l,mid);

    if(t[x<<1|1].mx1a==mx)tagModify(t[x].tg1,t[x].tg2,t[x].tg3,t[x].tg4,x<<1|1,mid+1,r);
    else tagModify(t[x].tg2,t[x].tg2,t[x].tg4,t[x].tg4,x<<1|1,mid+1,r);

    //清空tag
    t[x].tg1=t[x].tg2=t[x].tg3=t[x].tg4=0;
}

//初始化

void build(int x,int l,int r){
    if(l==r){
        t[x].sum=t[x].mx1a=t[x].mx1b=a[l];
        t[x].cnt=1;
        t[x].mx2a=-INF;
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);

    pushup(x);

}

//修改部分

void tagAdd(int x,int v,int l,int r){
    t[x].sum+=v*t[x].cnt+v*(r-l+1-t[x].cnt);
    t[x].mx1a+=v;
    t[x].mx1b=max(t[x].mx1b,t[x].mx1a);
    if(t[x].mx2a!=-INF)t[x].mx2a+=v;

    t[x].tg1+=v;t[x].tg2+=v;
    t[x].tg3=max(t[x].tg1,t[x].tg3);
    t[x].tg4=max(t[x].tg2,t[x].tg4);
}

void changeAdd(int x,int l,int r,int pl,int pr,int v){
    if(pl>r||pr<l)return ;
    if(l>=pl&&r<=pr){
        tagAdd(x,v,l,r);
        return ;
    }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    changeAdd(x<<1,l,mid,pl,pr,v);
    changeAdd(x<<1|1,mid+1,r,pl,pr,v);
    pushup(x);

}

void tagMin(int x,int v){
    int tmp=t[x].mx1a-v;
    t[x].sum-=t[x].cnt*tmp;
    t[x].mx1a=v;
    t[x].tg1-=tmp;
}

void changeMin(int x,int l,int r,int pl,int pr,int v){
    if(pl>r||pr<l||v>=t[x].mx1a)return ;//注意多了条件!!!
    if(l>=pl&&r<=pr&&t[x].mx2a<v){//注意多了条件!!!
        tagMin(x,v);
        return ;
    }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    changeMin(x<<1,l,mid,pl,pr,v);
    changeMin(x<<1|1,mid+1,r,pl,pr,v);
    pushup(x);
}

//查询部分

int querySum(int x,int l,int r,int pl,int pr){
    if(pl>r||pr<l)return 0;
    if(l>=pl&&r<=pr)return t[x].sum;
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    return querySum(x<<1,l,mid,pl,pr)+querySum(x<<1|1,mid+1,r,pl,pr);
}

int queryMaxA(int x,int l,int r,int pl,int pr){
    if(pl>r||pr<l)return -INF;
    if(l>=pl&&r<=pr)return t[x].mx1a;
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    return max(queryMaxA(x<<1,l,mid,pl,pr),queryMaxA(x<<1|1,mid+1,r,pl,pr));
}


int queryMaxB(int x,int l,int r,int pl,int pr){
    if(pl>r||pr<l)return -INF;
    if(l>=pl&&r<=pr)return t[x].mx1b;
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    return max(queryMaxB(x<<1,l,mid,pl,pr),queryMaxB(x<<1|1,mid+1,r,pl,pr));
}

// void updateB(int x,int l,int r){
//     if(l==r){
//         tb[x].v=max(tb[x].v,ta[x].v);
//         return ;
//     }

//     int mid=(l+r)>>1;
//     updateB(x<<1,l,mid);
//     updateB(x<<1|1,mid+1,r);
//     tb[x].mx=max(tb[x<<1].mx,tb[x<<1|1].mx);
// }


signed main() {
    // freopen("P6242_1.in", "r", stdin);
    // freopen("chfran.out", "w", stdout);
    // cerr<<1.00*(&f2-&f1)/1024/1024<<endl;
    n=rd;m=rd;
    for(int i=1;i<=n;i++)a[i]=rd;

    build(1,1,n);
    // int c=0;
    while(m--){
        // cerr<<"id="<<++c<<' ';
        int op=rd,l=rd,r=rd;
        if(op==1){
            int k=rd;
            changeAdd(1,1,n,l,r,k);
        }if(op==2){
            int v=rd;
            changeMin(1,1,n,l,r,v);
        }if(op==3){
            cout<<querySum(1,1,n,l,r)<<endl;

        }if(op==4){
            cout<<queryMaxA(1,1,n,l,r)<<endl;
        }if(op==5){
            cout<<queryMaxB(1,1,n,l,r)<<endl;

        }

    }
    return 0;
}

/*
1
2 5 1 
0 0 1 
0 0 4 

*/