xtu oj 1238 线段树

这篇博客介绍了XTU OJ 1238题目的线段树解法,重点讨论了如何使用lazy标记处理操作2和3的巧妙之处。博主详细解释了线段树的lazy标记升级,并给出了关键的pushDown函数实现,确保在更新子节点时保持最大值和最小值的正确范围。




做了我好长时间啊,这个线段树还是很经典的

题意:

四种操作,题目写的很清楚,就不说了,主要是这个题目对于操作2 3 用lazy标记很巧妙

题解:

线段树,lazy标记升级

void pushDown(int p)
{
    if(lazy[p])
    {
        lazy[p<<1]+=lazy[p];
        lazy[p<<1|1]+=lazy[p];

        sum[p<<1]+=lazy[p];
        sum[p<<1|1]+=lazy[p];

        mins[p<<1]+=lazy[p];
        mins[p<<1|1]+=lazy[p];

        lazy[p]=0;
    }

    sum[p<<1|1]=min(sum[p<<1|1],sum[p]);
    sum[p<<1|1]=max(sum[p<<1|1],mins[p]);
    mins[p<<1|1]=max(mins[p<<1|1],mins[p]);
    mins[p<<1|1]=min(mins[p<<1|1],sum[p]);

    sum[p<<1]=min(sum[p<<1],sum[p]);
    sum[p<<1]=max(sum[p<<1],mins[p]);
    mins[p<<1]=max(mins[p<<1],mins[p]);
    mins[p<<1]=min(mins[p<<1],sum[p]);
}

后面的那8个式子子,是向下传递最大最小值

若父亲节点的最大值是max   最小值是min,那么子节点的最大最小值一定在min~max范围内

比如说:子节点的最小值要么等于min,要么大于min,若大于min的时候就要注意了,不能大于父亲节点的最大值

满足这些条件就可以得到一个完美的lazy标记的pushDown函数了


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define MAXN 200010
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define mem(a) memset(a,0,sizeof(a[0]))
#define LL long long
LL min(LL x,LL y){return x<y?x:y;}
LL max(LL x,LL y){return x>y?x:y;}
LL sum[MAXN<<2];
LL mins[MAXN<<2];
LL lazy[MAXN<<2];

void pushUp(int p)
{
    sum[p]=max(sum[p<<1],sum[p<<1|1]);
    mins[p]=min(mins[p<<1],mins[p<<1|1]);
}
void pushDown(int p)
{
    if(lazy[p])
    {
        lazy[p<<1]+=lazy[p];
        lazy[p<<1|1]+=lazy[p];

        sum[p<<1]+=lazy[p];
        sum[p<<1|1]+=lazy[p];

        mins[p<<1]+=lazy[p];
        mins[p<<1|1]+=lazy[p];

        lazy[p]=0;
    }

    sum[p<<1|1]=min(sum[p<<1|1],sum[p]);
    sum[p<<1|1]=max(sum[p<<1|1],mins[p]);
    mins[p<<1|1]=max(mins[p<<1|1],mins[p]);
    mins[p<<1|1]=min(mins[p<<1|1],sum[p]);

    sum[p<<1]=min(sum[p<<1],sum[p]);
    sum[p<<1]=max(sum[p<<1],mins[p]);
    mins[p<<1]=max(mins[p<<1],mins[p]);
    mins[p<<1]=min(mins[p<<1],sum[p]);
}
void build(int l,int r,int p)
{
    lazy[p]=0;///重要,不知道memset和这个有什么区别
    if(l==r){
        scanf("%I64d",&sum[p]);
        mins[p]=sum[p];
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushUp(p);
}

LL ans_max,ans_min;
void query(int l,int r,int p,LL i,LL j)
{
    if(l>=i&&r<=j){
        ans_max=max(ans_max,sum[p]);
        ans_min=min(ans_min,mins[p]);
        return ;
    }

    pushDown(p);///防止有值没有传下去
    int mid=(l+r)>>1;
    if(mid>=i) query(lson,i,j);
    if(mid<j) query(rson,i,j);
    pushUp(p);
}

void update_1(int l,int r,int p,LL i,LL j,LL num)
{
    if(l>=i&&r<=j){
        sum[p]+=num;
        mins[p]+=num;
        lazy[p]+=num;
        return ;
    }

    pushDown(p);
    int mid=(l+r)>>1;
    if(mid>=i)
        update_1(lson,i,j,num);
    if(mid<j)
        update_1(rson,i,j,num);
    pushUp(p);
}

void update_2(int l,int r,int p,LL i,LL j,LL num)
{
    if(l>=i&&r<=j){
        sum[p]=min(sum[p],num);
        mins[p]=min(mins[p],num);
        return ;
    }

    pushDown(p);
    int mid=(l+r)>>1;
    if(mid>=i)
        update_2(lson,i,j,num);
    if(mid<j)
        update_2(rson,i,j,num);
    pushUp(p);
}

void update_3(int l,int r,int p,LL i,LL j,LL num)
{
    if(l>=i&&r<=j){
        sum[p]=max(sum[p],num);
        mins[p]=max(mins[p],num);
        return ;
    }

    pushDown(p);
    int mid=(l+r)>>1;
    if(mid>=i)
        update_3(lson,i,j,num);
    if(mid<j)
        update_3(rson,i,j,num);
    pushUp(p);
}

int main()
{
    int n,q,T;
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        mem(sum);
        mem(mins);
        scanf("%d%d",&n,&q);
        build(1,n,1);

        LL temp,a,b,c;
        while(q--)
        {
            scanf("%I64d",&temp);
            if(temp==1){
                scanf("%I64d%I64d%I64d",&a,&b,&c);
                update_1(1,n,1,a,b,c);
            }else if(temp==2){
                scanf("%I64d%I64d%I64d",&a,&b,&c);
                update_2(1,n,1,a,b,c);
            }else if(temp==3){
                scanf("%I64d%I64d%I64d",&a,&b,&c);
                update_3(1,n,1,a,b,c);
            }else{
                scanf("%I64d%I64d",&a,&b);
                ans_max=-0x3f3f3f3f3f3f;
                ans_min=0x3f3f3f3f3f3f;
                query(1,n,1,a,b);
                printf("%I64d %I64d\n",ans_min,ans_max);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值