【模板】线段树

线段树

前情提要

上周讲的线段树专题,没有太跟上,自己又把线段树的模板先敲了几遍,又增加了些许改进,用此博客以保存。

线段树模板

#include <iostream>
#include <cstdio>
#include <cmath>
#define N 200000
using namespace std;
//@start: 2020-03-17 21:43:16
//@end:   2020-03-19 22:22:10
/**
 * 线段树模板v2.0
 * 更新要点:
 *      1. 代码简约化
 *      2. 增加代码注释的详细度
 *      3. 增加较为完备的函数参数注释
 **/
int num[N];     //存放数据
int T[N<<2];    //线段树
int lazy[N<<2]; //懒数组,用于区间更新
/*
参数说明:
    t:要更新的根节点
*/
void push_up(int t)
{//向上更新
    T[t]=T[t<<1]+T[t<<1|1];
}

/*
参数说明:
    t:要更新的根节点
    d:根节点所管辖的区间长度,r-l+1
*/
void push_down(int t,int d)
{//向下更新,即下推懒标记
    if(lazy[t])
    {//如果当前根节点存在懒标记
        //先下发给左右子节点
        lazy[t<<1] += lazy[t];
        lazy[t<<1|1] += lazy[t];
        //左右子节点数值加上懒标记乘以管辖区间长度
        T[t<<1] += lazy[t]*(d-(d>>1));
        T[t<<1|1] += lazy[t]*(d>>1);
        //当前根节点懒标记归零
        lazy[t]=0;
    }
}

/*
参数说明:
    l,r:建树左右边界
    t:建树根节点
*/
void build(int l,int r,int t)
{//建树
    if(l==r)
    {//找到叶子节点
        T[t]=num[l];//赋值
        return ;
    }
    int mid=(l+r)>>1;//取中值
    build(l,mid,t<<1);//左子树
    build(mid+1,r,t<<1|1);//右子树
    push_up(t);//建完向上更新
    return ;
}

/*
参数说明:
    p,v:要更新的位置,更新的值
    l,r:根节点左右边界
    t:根节点
*/
void update(int p,int v,int l,int r,int t)
{//单点更新
    if(l==r)
    {//找到叶子节点
        T[t]=v;
        return ;
    }
    int mid=(l+r)>>1;//取中值
    //没找到叶子节点
    if(p<=mid)//该点在左子区间
        update(p,v,l,mid,t<<1);
    else      //该点在右子区间
        update(p,v,mid+1,r,t<<1|1);
    push_up(t);//找完需要向上更新
}

/*
参数说明:
    L,R:待更新区间
    l,r:根节点左右边界
    t:根节点
*/
void update_qj(int L,int R,int v,int l,int r,int t)
{//区间更新
    int d=r-l+1;//当前节点管辖长度
    if(l==L&&r==R)
    {//找到要更新的点或区间
        //更新数据及懒标记
        T[t]+=d*v;
        lazy[t]+=v;
        return ;
    }
    push_down(t,d);//在查询之前向下更新
    int mid=(l+r)>>1;//取中值
    if(R<=mid)//更新区间完全在左子区间内
        update_qj(L,R,v,l,mid,t<<1);
    else if(L>mid)//更新区间完全在右子区间内
        update_qj(L,R,v,mid+1,r,t<<1|1);
    else//更新区间横跨左右子区间
    {//Tip!!!:记得把更新区间也要从mid处断开
        update_qj(L,mid,v,l,mid,t<<1);
        update_qj(mid+1,R,v,mid+1,r,t<<1|1);
    }
    push_up(t);
    return ;
}

/*
参数说明:
    L,R:待查询区间
    l,r:根节点左右边界
    t:根节点
*/
int query(int L,int R,int l,int r,int t)
{//查询
    if(L==l&&R==r)//找到
        return T[t];
    push_down(t,r-l+1);
    int mid=(l+r)>>1;//取中值
    //加法的线段树查询写法
    if(R<=mid)//查询区间完全在左子区间内
        return query(L,R,l,mid,t<<1);
    else if(L>mid)//查询区间完全在右子区间内
        return query(L,R,mid+1,r,t<<1|1);
    else//查询区间横跨左右子区间
        return query(L,mid,l,mid,t<<1)+query(mid+1,R,mid+1,r,t<<1|1);
    //Tip!!!:记得把更新区间也要从mid处断开
}

int main()
{
    /**
     * 1. 输入数据总数n
     * 2. 输入所有数据
     * 3. build建造
     * 4. 根据题意操作
     **/
    return 0;
}
posted @ 2020-03-20 11:25  熊子q  阅读(100)  评论(0)    收藏  举报