模板~线段树

不重点介绍线段树概念了,就大概贴一下几部分的模板

build();             建立线段树

update();          更新操作(单点、区间更新)

query();            查询操作(单点、区间查询)

pushup();         向上回溯

pushdown();    向下延时更新

先说明一下数组

int s[maxx],mx[maxx<<2];   //s为输入数组,mx为求和或最值数组
int lazy[maxx<<2];         //lazy为延迟标记

首先,build线段树

void build(int l,int r,int rt)  //rt为节点编号,l,r为节点区间
{
    if(l==r)      //寻找到一个叶节点
    {
        mx[rt]=s[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);      //左右递归
    build(mid+1,r,rt<<1|1); 
    pushup(rt);         //向上回溯    
}

pushup

void pushup(int rt)   //向上回溯
{
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}

update分为单点更新和区间更新(需要延迟标记)

先来简单一些的单点更新

void update(int L,int s,int l,int r,int rt)   //单点更新
{
    if(l==r)                                 //找到叶节点,进行修改
    {
        mx[rt]=s;
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid)  update(L,s,l,mid,rt<<1);        //判断调用左子树还是右子树
    else        update(L,s,mid+1,r,rt<<1|1);
    pushup(rt);                                //向上回溯
}

接下来的区间更新需要用到延迟标记,这里用lazy表示

当有[l,r]区间内的值都需要改变时,若是使用单点更新会极大浪费时间,增加复杂度,这里lazy就是为了降低时间复杂度而存在,是线段树比较快的精华所在

第一次学lazy怎么都理解不了,这次学习看了大牛的博客后,个人觉得线段树的lazy标记和差分数组很像

条件都是需要改变一个区间内的所有值,使用一个特定的数组,在特定位置进行标记,之后在需要展开计算的时候,再进行所有的计算

lazy标记的本节点已经更新,但是其子节点还需要更新,其余代码里详述

(差分数组就不仔细介绍了,毕竟线段树模板)

void Update(int L,int R,int c,int l,int r,int rt)  //区间更新
{
    if(L<=l && R>=r){                            //找到可更新区间
        mx[rt]+=c*(r-l+1);                       //本节点加上其所有子节点的值
        lazy[rt]+=c;                             //对本节点lazy进行标记
        return ;
    }
    int mid=(l+r)>>1;                            
    pushdown(rt,mid-l+1,r-mid);                  //向子节点延时更新
    if(L<=mid)  Update(L,R,c,l,mid,rt<<1);       //判断调用左子树还是右子树
    if(R>mid)   Update(L,R,c,mid+1,r,rt<<1|1);
    pushup(rt);
}

pushdown 向下延时更新

void pushdown(int rt,int l,int r)  //向下延时更新
{
    if(lazy[rt]){                  //因为可能已经有值,所以这里都是 +=
        lazy[rt<<1]+=lazy[rt];     //左子节点lazy标记更新
        lazy[rt<<1|1]+=lazy[rt];   //右子节点lazy标记更新
        mx[rt<<1]+=lazy[rt]*l;     //左子节点进行更新
        mx[rt<<1|1]+=lazy[rt]*r;   //右子节点进行更新
        lazy[rt]=0;                //将该标记清零
    }
}

最后还有query

单点查询

int query(int L,int l,int r,int rt)                       //单点查询
{
    if(l==r)  return mx[rt];                              //找到节点直接返回
    int mid=(l+r)>>1;
    int ret=0;
    if(L<=mid)  ret=max(ret,query(L,l,mid,rt<<1));        //判断调用左子树还是右子树
    else        ret=max(ret,query(L,mid+1,r,rt<<1|1));
    return ret;
}

区间查询

int query(int L,int R,int l,int r,int rt)                 //区间查询
{
    if(L<=l && R>=r) return mx[rt];                       //区间内直接返回
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);                           //向下更新,否则结果可能不正确
    int ret=0;
    if(L<=mid)  ret=max(ret,query(L,R,l,mid,rt<<1));      //判断调用左子树右子树
    if(R>mid)   ret=max(ret,query(L,R,mid+1,r,rt<<1|1));
    return ret;
}

以上都是递归版的线段树

(非递归版的还没有学习)

猜你喜欢

转载自blog.csdn.net/renzijing/article/details/81214703
今日推荐