线段树模板(有注释)

线段树模板(有注释)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define N 100000
using namespace std;
int a[N],Sum[N<<2],Add[N<<2];//开4倍大的数组构建线段树
void Pushup(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}//向上传值
void PushDown(int rt,int ln,int rn)//向下传懒标记并更新值,ln为左子树结点数,rn为右子树结点数
{
    if(Add[rt])//当懒标记不为0时
    {
        Add[rt<<1]+=Add[rt];//将懒标记传给左孩子
        Add[rt<<1|1]+=Add[rt];//将懒标记传给右孩子
        Sum[rt<<1]+=Add[rt]*ln;//更新左子树的值
        Sum[rt<<1|1]+=Add[rt]*rn;//更新右子树的值
        Add[rt]=0;//将当前结点的懒标记置为0
    }
}
void Build(int l,int r,int rt)//建树
{
    if(l==r)//遍历到叶结点时直接存数据
    {
        Sum[rt]=a[l];
        return ;
    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);//建左子树
    Build(m+1,r,rt<<1|1);//建右子树
    Pushup(rt);//向上传值
}
void Update1(int L,int C,int l,int r,int rt)//点修改,a[L]+=C
{
    if(l==r)//当为叶节点时直接更新结点的值
    {
        Sum[rt]+=C;
        return ;
    }
    int m=(l+r)>>1;
    if(L<=m)Update1(L,C,l,m,rt<<1);//若结点在左子树,更新左子树
    else Update1(L,C,m+1,r,rt<<1|1);//若结点在右子树,更新右子树
    Pushup(rt);//向上传值
}
void Update2(int L,int R,int C,int l,int r,int rt)//区间修改
{
    if(L<=l&&R>=r)//当要修改的区间覆盖结点区间时,当前结点Sum值直接加上结点数*C
    {
        Sum[rt]+=C*(r-l+1);
        Add[rt]+=C;
        return ;
    }
    int m=(l+r)>>1;
    PushDown(rt,m-l+1,r-m);//向下传递懒标记并更新值
    if(L<=m)Update2(L,R,C,l,m,rt<<1);//更新左子树
    if(R>m)Update2(L,R,C,m+1,r,rt<<1|1);//更新右子树
    Pushup(rt);//向上传值
}
int Query(int L,int R,int l,int r,int rt)//区间访问
{
    if(L<=l&&R>=r)//当要修改的区间覆盖结点区间时,直接访问
    {
        return Sum[rt];
    }
    int m=(l+r)>>1;
    PushDown(rt,m-l+1,r-m);//避免懒标记没有处理
    int ans=0;
    if(L<=m)ans+=Query(L,R,l,m,rt<<1);//返回左子树的值
    if(R>m)ans+=Query(L,R,m+1,r,rt<<1|1);//返回右子树的值
    return ans;
}
int Findmax(int L,int R,int l,int r,int rt)//查询区间最大值,是找最大值的区间,即最长的线段
{
    if(L<=l&&R>=r)//当要修改的区间覆盖结点区间时,直接访问
    {
        return Sum[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m)ans=max(ans,Findmax(L,R,l,m,rt<<1));//求左子树最大值区间
    if(R>m)ans=max(ans,Findmax(L,R,m+1,r,rt<<1|1));//求右子树最大值区间
    return ans;
}
int main()
{
    int n,ope,L,C,R;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    Build(1,n,1);//建树
    while(cin>>ope)
    {
        if(ope==0)break;//退出循环
        else if(ope==1)//点修改
        {
            cin>>L>>C;
            Update1(L,C,1,n,1);
            cout<<"结点"<<L<<"修改成功"<<endl;
        }
        else if(ope==2)//区间修改
        {
            cin>>L>>R>>C;
            Update2(L,R,C,1,n,1);
            cout<<"区间"<<L<<'——'<<R<<"修改成功"<<endl;
        }
        else if(ope==3)//区间访问
        {
            cin>>L>>R;
            cout<<"区间"<<L<<"——"<<R<<"的值为"<<Query(L,R,1,n,1)<<endl;
        }
        else if(ope==4)//查询区间最大值
        {
            cin>>L>>R;
            cout<<"区间"<<L<<"——"<<R<<"的最大值为"<<Findmax(L,R,1,n,1)<<endl;
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值