算法笔记——线段树

 线段树节点的主要参数

①左端点        ②右端点        ③维护的区间信息        ④Lazy_tag

线段树的创建

采用递归创建线段树

①若是叶子节点(l=r),则节点的最值就是对应位置的元素值

②若是非叶子节点,则递归创建左子树和右子树

③回溯过程中,通过节点左右子树的区间信息维护该节点的区间信息(意味着除了建树每次更新都需要递归维护)

#define lson (i<<1)
#define rson (i<<1|1)
#define mid ((tr[i].l+tr[i].r)>>1)
void build(int i,int x,int y)//节点编号、左端点值、右端点值
{
    tr[i].l=x;tr[i].r=y;
    if(x==y){tr[i].sum=a[x];    return;}//注意这里a[x]易错写成a[i]
    build(lson,x,mid);//左区间长≥右区间长
    build(rson,mid+1,y);
    tr[i].sum=tr[lson].sum+tr[rson].sum;
}

Lazy_tag

在区间查询和更新中,若节点区间被查询区间覆盖,可以直接只更新当前节点的信息,再打上一个Lazy_tag,等后续节点被访问到后再下传,降低复杂度。

打上Lazy_tag的当前节点区间信息已经被更新,子树节点未被更新,访问到该节点时即使下传Lazy_tag信息即可。下传过程即为更新两个子节点区间信息并打上Lazy_tag的过程

注意:当前节点的Lazy_tag传到下层以后要删除,防止重复下传

Lazy_tag本质上代表着有关该节点的最新操作,因此处理到该节点时要不断更新,保证传递给子节点的是最新的更新情况

void push_down(int i)
{
    if(!tr[i].add)    return;
    tr[lson].add+=tr[i].add;
    tr[rson].add+=tr[i].add;
    tr[lson].sum+=tr[i].add*(len-len/2);//左区间是长的那个
    tr[rson].sum+=tr[i].add*len/2;
    tr[i].add=0;
}

区间查询

①若节点所在的区间被查询区间[l,r]覆盖,则返回该节点的区间信息

②判断是在左子树中还是在右子树中继续递归查询(可能两棵子树都要查询),并下传Lazy_tag为后续可能访问到的节点做准备

③回溯时返回区间信息

int ask(int i,int x,int y)
{
    if(tr[i].l>=x && tr[i].r<=y)    return tr[i].sum;
    push_down(i);
    int ans=0;
    if(x<=mid)    ans+=ask(lson,x,y);
    if(y>mid)     ans+=ask(rson,x,y);
    return ans;    
}

区间更新

①若当前节点的区间被查询区间[l,r]覆盖,则仅对该节点进行更新并做Lazy_tag,表示该节点已被更新,该节点的子节点暂不更新。

②判断是在左子树还是右子树中继续递归查询(可能两棵子树都要查询),并下传Lazy_tage为后续可能访问到的节点做准备。

③回溯时维护节点的区间信息

#define len (tr[i].r-tr[i].l+1)
void updata(int i,int x,int y,int k)
{
    if(tr[i].l>=x && tr[i].r<=y)
    {
        tr[i].sum+=len*k;
        tr[i].add+=k;
        return;
    }
    push_down(i);
    if(x<=mid)    updata(lson,x,y,k);
    if(y>mid)     updata(rson,x,y,k);
    tr[i].sum=tr[lson].sum+tr[rson].sum;
}

线段树数组要开4倍空间

由线段树的性质易得,若区间长度为N,则线段树的叶子节点有N个(l==r的情况),他们只能分布在最后一层和倒数第二层。

假设最后一层的叶子结点有m个,倒数第二层的有n个,m+n=N

因为n≤N,根据二叉树的性质,除了最后一层的m个,节点总数≤2N-1

如果所有的第二层节点都有2个子节点(即无叶子节点),则m≤2n≤4N-2

然而两个等号不可能同时取到,因此开4倍空间是安全的。

时间复杂度

更新和查询均为O(logn)

注意点

①Lazy_tag下传后要删除

②建树赋值时a[x]不要写成a[i]

③数组开4倍大小

④递归到下层时下传Lazy_tag,更新操作时回溯上传维护区间信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值