区间求和(线段树和树状数组)

本文详细介绍了线段树的基本概念、构建方法、点更新及区间更新操作,并通过实例解析了懒惰标记思想的应用。同时,还介绍了树状数组的原理与实现方式,包括更新和查询操作。

一. 线段树

线段树从零开始

线段树详解

lazy标记思想

一种二叉树

开四倍空间

#define N 1000000
int sum[N<<2];//求和
int a[N];//存原数组

 

1.建树(存和)

 

(1).递归实现

void PushUp(int rt)//更新{Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}  
void Build(int l,int r,int rt)
{ //[l,r]当前区间,rt表示当前节点位置 
    if(l==r) {//若到达叶节点 
        sum[rt]=a[l];//存储A数组的值
        return;  
    }  
    int m=(l+r)>>1;  
    Build(l,m,rt<<1);  //左边
    Build(m+1,r,rt<<1|1);  //右边
    PushUp(rt);  
}  

 

(2).非递归实现

 

void Build(int n){   
    N=1;
while(N < n+2) N <<= 1;   //原数组下标+N=存储下标 

    for(int i=1;i<=n;++i) sum[N+i]=a[i];   
    for(int i=N-1;i>0;--i){  
        sum[i]=sum[i<<1]+sum[i<<1|1];  
        Add[i]=0;  
    }  
}   

 

2.点更新

 

a[L]+=C

(1).

void Update(int L,int C,int l,int r,int rt)
{
    if(l==r){//到达叶节点,修改叶节点的值
        sum[rt]+=C;  
        return;  
    }  
    int m=(l+r)>>1;  
    if(L <= m) Update(L,C,l,m,rt<<1);  //根据条件判断往左子树调用还是往右
    else       Update(L,C,m+1,r,rt<<1|1);  
    PushUp(rt);//子节点的信息更新然后更新本节点
}   

 

(2).

 

void Update(int L,int C){  
    for(int s=N+L;s;s>>=1){  
        sum[s]+=C;  
    }  
}   

 

3.区间更新

 

a[L,R]+C

void PushDown(int rt,int ln,int rn){  
    //ln,rn为左子树,右子树的数字数量。   
    if(Add[rt]){  
         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;  //清除标记   

    }  
}  

 

 

 

void Update(int L,int R,int C,int l,int r,int rt){
    if(L <= l && r <= R){  
        sum[rt]+=C*(r-l+1);//更新数字和 
        Add[rt]+=C;//增加Add标记,子区间的Sum仍需要根据Add的值来调整  
        return ;   
    }  
    int m=(l+r)>>1;  
    PushDown(rt,m-l+1,r-m);//下推标记  
    //左右子树跟[L,R]有交集则递归
    if(L <= m) Update(L,R,C,l,m,rt<<1);  
    if(R >  m) Update(L,R,C,m+1,r,rt<<1|1);   
    PushUp(rt);//更新本节点信息   
}   

二. 树状数组

sum[ ]存...如图所示这种

然后神奇的x&(-x)

就求出最小1的位

int lowbit(int x)

{

    return x&(-x);

}//

更新

void update(int x, int num)

{

    while(x<=n)

        {

            c[x] += num;

            x += lowbit(x);

        }

}

 

int getsum(int x)

{

    int s = 0;

    while(x>0)

        {

            s += c[x];

            x -= lowbit(x);

        }

        return s;

}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值