线段树总结

背景:记录区间值,便于查询

常见应用:维护区间和,区间最大值

常见操作:点值更新,区间更新,区间查询

tip:lazy标记,先更新父节点的标记情况,等到有需要的时候再去更新孩子的标记情况

值得注意的地方:两个函数:push_up(int root)自底向下更新父亲节点;push_down(int root,int len)自父亲向下更新lazy标记

在递归下降过程中,当我们更新了叶子节点的信息以后,一定要记得push_up一下更新父亲节点信息

需要的空间 int a[MaxNum<<2]

板子:

#include <bits/stdc++.h>

using namespace std;


//l,r:区间左右端点 root:当前结点在内存池中的位置
void build(int l,int r,int root)
{
    if(l==r)
    {
        num[root]=1;
        return;
    }

    //[l,mid]左子树, [mid+1,r]:右子树
    int mid=(l+r)>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);

    push_up(root);
    //先将左右子树都算出来,再来更新它的值
    //沿路回溯,回溯到点root,都是被[la,lb]或其子区间影响到到的点,边回溯边更新

}   //调用build[1,N,1]


//根结点状态更新
void push_up(int root)
{
    num[root]=num[root<<1]+num[root<<1|1];
}

//线段树点的修改(单点更新)
void upgrade(int p,int val,int l,int r,int root)
{
    if(l==r)
    {
        num[root]+=val;
        return;
    }

    int mid=(l+r)>>1;
    if(p>mid)
        upgrade(p,val,mid+1,r,root<<1|1);
    else
        upgrade(p,val,mid,root<<1);

    //沿路回溯
    //num[root]=num[root<<1]+num[root<<1|1];
    push_up(root);
}

//线段树的区间修改
//打上lazy标记的点自己本身已经更新了
//[la,lb]为需要更性的区间(一直不变)
//[l,r]为当前区间的左右端点(随递归更新)
//root为当前[l,r]对映的根存储位置(随递归更新)
void upgrade_2(int la,int rb,int l,int r,int val,int root)
{
    if(la<=l&&rb>=r)
    {
        num[root]=(r-l+1)*val;
        lazy[root]=val;
        return;
    }

    push_down(root,r-l+1);
    //在继续递归之前先把root标记往下推

    int mid=(l+r)>>1;
    if(la<=mid)
        upgrade_2(la,rb,l,mid,val,root<<1);
    if(rb>mid)
        upgrade_2(la,rb,mid+1,r,val,root<<1|1);

    push_up(root);
}


//push_up函数:假设两个子节点的信息完全知道,推出父节点的信息
//push_down函数:与lazy的使用有关
//传入父节点root和区间长度len
void push_down(int root,int len)
{
    if(lazy[root]==0)
        return;

    lazy[root<<1]=lazy[root];
    lazy[root<<1|1]=lazy[root];

    num[root<<1]=lazy[root<<1]*(len-(len)/2);
    num[root<<1|1]=lazy[root<<1|1]*len/2;

    lazy[root]=0;
}

//查询区间[la,rb]的值
int  query(int la,int rb,int l,int r,int root)
{
    if(l>=la&&r<=rb)
        return num[root];

    push_down(root,r-l+1);

    int mid=(l+r)<<1;
    int ans=0;
    if(la<=mid)
        ans+=query(la,rb,l,mid,root<<1);
    if(rb>mid)
        ans+=query(la,rb,mid+1,r,root<<1|1);

    return ans;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值