线段树

转自:点击打开链接
以下
以维护数列区间和的线段树为例,演示最基本的线段树代码。

(0)定义:

  1. #define maxn 100007  //元素总个数  
  2. #define ls l,m,rt<<1  
  3. #define rs m+1,r,rt<<1|1  
  4. int Sum[maxn<<2],Add[maxn<<2];//Sum求和,Add为懒惰标记   
  5. int A[maxn],n;//存原数组数据下标[1,n]   

(1)建树:

  1. //PushUp函数更新节点信息 ,这里是求和  
  2. void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}  
  3. //Build函数建树   
  4. void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号  
  5.     if(l==r) {//若到达叶节点   
  6.         Sum[rt]=A[l];//储存数组值   
  7.         return;  
  8.     }  
  9.     int m=(l+r)>>1;  
  10.     //左右递归   
  11.     Build(l,m,rt<<1);  
  12.     Build(m+1,r,rt<<1|1);  
  13.     //更新信息   
  14.     PushUp(rt);  
  15. }  


(2)点修改:

假设A[L]+=C:
  1. void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号  
  2.     if(l==r){//到叶节点,修改   
  3.         Sum[rt]+=C;  
  4.         return;  
  5.     }  
  6.     int m=(l+r)>>1;  
  7.     //根据条件判断往左子树调用还是往右   
  8.     if(L <= m) Update(L,C,l,m,rt<<1);  
  9.     else       Update(L,C,m+1,r,rt<<1|1);  
  10.     PushUp(rt);//子节点更新了,所以本节点也需要更新信息   
  11. }   

(3)区间修改:


假设A[L,R]+=C
  1. void Update(int L,int R,int C,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号   
  2.     if(L <= l && r <= R){//如果本区间完全在操作区间[L,R]以内   
  3.         Sum[rt]+=C*(r-l+1);//更新数字和,向上保持正确  
  4.         Add[rt]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整  
  5.         return ;   
  6.     }  
  7.     int m=(l+r)>>1;  
  8.     PushDown(rt,m-l+1,r-m);//下推标记  
  9.     //这里判断左右子树跟[L,R]有无交集,有交集才递归   
  10.     if(L <= m) Update(L,R,C,l,m,rt<<1);  
  11.     if(R >  m) Update(L,R,C,m+1,r,rt<<1|1);   
  12.     PushUp(rt);//更新本节点信息   
  13. }   

(4)区间查询:

询问A[L,R]的和
首先是下推标记的函数:
  1. void PushDown(int rt,int ln,int rn){  
  2.     //ln,rn为左子树,右子树的数字数量。   
  3.     if(Add[rt]){  
  4.         //下推标记   
  5.         Add[rt<<1]+=Add[rt];  
  6.         Add[rt<<1|1]+=Add[rt];  
  7.         //修改子节点的Sum使之与对应的Add相对应   
  8.         Sum[rt<<1]+=Add[rt]*ln;  
  9.         Sum[rt<<1|1]+=Add[rt]*rn;  
  10.         //清除本节点标记   
  11.         Add[rt]=0;  
  12.     }  
  13. }  

然后是区间查询的函数:
  1. int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号  
  2.     if(L <= l && r <= R){  
  3.         //在区间内,直接返回   
  4.         return Sum[rt];  
  5.     }  
  6.     int m=(l+r)>>1;  
  7.     //下推标记,否则Sum可能不正确  
  8.     PushDown(rt,m-l+1,r-m);   
  9.       
  10.     //累计答案  
  11.     int ANS=0;  
  12.     if(L <= m) ANS+=Query(L,R,l,m,rt<<1);  
  13.     if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);  
  14.     return ANS;  
  15. }   

(5)函数调用:


  1. //建树   
  2. Build(1,n,1);   
  3. //点修改  
  4. Update(L,C,1,n,1);  
  5. //区间修改   
  6. Update(L,R,C,1,n,1);  
  7. //区间查询   
  8. int ANS=Query(L,R,1,n,1); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值