Segment tree template code + detailed notes

#include<iostream>
#include<cstdio>
#define N 50005
#define mid  ((l + r )>>1)
using namespace std;
int num[N];//数据数组,用于存放数据
int tree[N<<2];//线段树数组,用于构造线段树
int lazy[N<<2];//懒惰标记,用于区间更新

//向上更新,t为节点下标
void PushUp(int t)
{
    tree[t]=tree[t<<1]+tree[t<<1|1];
    return ;
}

//向下更新,r为根节点,lr为r的管理区间左右边界
void PushDown(int l,int r,int t)
{
    if(lazy[t])
    {//如果有lazy值则下推
        //lazy值下推给左右节点
        lazy[t<<1]+=lazy[t];
        lazy[t<<1|1]+=lazy[t];
        //左右节点的值也要改变
        //改变为 lazy值*管理节点个数
        tree[t<<1]+=lazy[t]*(mid-r+1);
        tree[t<<1|1]+=lazy[t]*(l-mid);
        lazy[t]=0;
    }
}

//建树,lr为建树左右区间边界,t为根节点
void Build(int l,int r,int t)
{
    if(l==r)
    {//找到叶子节点
        tree[t]=num[l];//赋值
        return ;
    }
    Build(l,mid,t<<1);//建左子树
    Build(mid+1,r,t<<1|1);//建右子树
    PushUp(t);//更新数据
    return ;
}

//单点更新,p为更新位置,v为更新值,lr为根节点管理左右区间,t为根节点
void UpdatePoint(int p,int v,int l,int r,int t)
{
    if(l==r&& l == p)
    {//找到叶子节点
        tree[t]+=v;
        return ;
    }
    if(p<=mid)//目标点在左子区间
        UpdatePoint(p,v,l,mid,t<<1);
    else //目标点在左子区间
        UpdatePoint(p,v,mid+1,r,t<<1|1);
    PushUp(t);//更新数据
    return ;
}

//区间更新,LR为更新区间左右边界,v为要加上的值,lr为根节点管理区间左右边界,t为根节点
void UpdateRange(int L,int R,int v,int l,int r,int t)
{
    if(L<=l&&R>=r)
    {//如果找到一个能全部被包含于修改范围的区间
        lazy[t]+=v;//则加上懒惰标记
        tree[t]+=v*(r-l+1);//同时也要修改区间和,与pushdown的修改同理
        return ;
    }
    PushDown(l,r,t);//修改时也要将之前的懒惰标记下移
    //递归寻找能全被包含的区间
    if(L<=mid)UpdateRange(L,R,v,l,mid,t<<1);
    if(R>mid) UpdateRange(L,R,v,mid+1,r,t<<1|1);
    PushUp(t);
}

//区间查询,LR为查询区间左右边界,lr为根节点管理区间左右边界,t为根节点
int Query(int L,int R,int l,int r,int t)
{
    if(L<=l&&R>=r)
    {//找到目标区间
        return tree[t];
    }
    PushDown(l,r,t);//如果有lazy值要下推
    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,R,l,mid,t<<1)+Query(L,R,mid+1,r,t<<1|1);
}

int main()
{
    int t,n,a,b,k=1,v;
    string s;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>num[i];
        Build(1,n,1);
        printf("Case %d:\n",k++);
        while(cin>>s&&s!="End")
        {
            cin>>a>>b;
            if(s=="Query")
                cout<<Query(a,b,1,n,1)<<endl;
            else if(s=="Add")
                UpdatePoint(a,b,1,n,1);
            else if(s=="Sub")
                UpdatePoint(a,-b,1,n,1);
            else if(s=="Range")
            {cin>>v;UpdateRange(a,b,v,1,n,1);}
        }
    }
    return 0;
}



Published 67 original articles · won praise 42 · views 30000 +

Guess you like

Origin blog.csdn.net/qq_26235879/article/details/102573875