线段树一:修改点的值求任意区间的值

 

线段树一:修改点的值求任意区间的值

  427人阅读  评论(0)  收藏  举报

强烈推荐:这篇文章------------------------(完全版)线段树

线段树的作用:解决区间计算问题。

例如:记录一个区间的最值(最大或最小值)和总量,并在区间的插入、删除、修改中维护这些最值和总量。

线段树是一颗二叉树。记为T(a,b),a,b表示顶点T为区间[a,b]。区间长度b-a记为L。递归定义T[a,b]:

若区间长度L>=1,区间[a,(a+b)/2]为T的左孩子,区间((a+b)/2,b]为T的右孩子

如图为区间[1,10]的线段树:

线段树的基本操作:

(1)建立线段树T(a,b)。

(2)将区间[c,d]插入到线段树T(a,b)。

(3)将区间[c,d]从线段树T(a,b)中删除。

(4)对线段树进行动态维护。

以下代码转载自:himdd的文章

线段树的建立:

  1. int Pushup(int root) //把当前结点的信息更新到父节点   
  2. {   sum[root]=sum[root<<1]+sum[root<<1|1];  
  3. }  
  4. void Build(int L,int R,int root)  
  5. {   if(L==R)   
  6.     {   scanf("%d",&sum[root]);  
  7.         return ;  
  8.     }  
  9.     int mid=(L+R)>>1;  
  10.     Build(Lson); //左孩子   
  11.     Build(Rson); //右孩子   
  12.     Pushup(root);   
  13. }  

上面代码构造的树为,我没有画完整,但是已经能够体现问题了。

后面的数相当与这颗树的根,区间[1,10]的根设为1,所以[1,5]的就为1<<1=2,[6,10]的根为:(1<<1)+1=3,依次类推,然后用sum[root]表示根节点为root的所代表的这个区间的值,如:sum[1]保存的就是[1,10]这个区间的值。所以有:sum[1]=sum[2]+sum[3],即:sum[root]=sum[root>>1]+sum[(root>>1)+1]

线段树的修改:比方说我要修改某一个结点的值,那么我只需要沿着根结点1往下寻找,并记录这条路径,更新sum的值即可,比方说变化2:它需要修改的区间依次为:[1,10]

->[1,5]->[1,3]->[1,2]->[2,2]。具体操作见代码:

  1. void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val   
  2. {   if(L==R)  
  3.     {   sum[root]+=val;  
  4.         return ;  
  5.     }   
  6.     int mid=(L+R)>>1;  
  7.     if(q<=mid) Update(q,val,Lson); //说明p在左结点   
  8.     else Update(q,val,Rson); //说明p在右结点   
  9.     Pushup(root);  
  10. }  

至于统计某一区间[a,b]的值,那么同样可以利用二分的思想:

比方说要求[4,8],二分mid=(4+8)/2=6,然后它就可以写成:[4,6]+[7,8]=[4,5]+[6,6]+[7,7]+[8,8]=[4,4]+[5,5]+...+[8,8]。代码见下题中的Query部分。

HDU 1166(敌兵布阵),相当于树状数组中的插点问线,初始化给你每个结点的值,然后更新每个点的值,求给定的任意一个区间的值。

  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>   
  4. using namespace std;  
  5. const int MAX=50010;  
  6. #define Lson L,mid,root<<1 //遇到Lson的时候强制替换为后面的语句   
  7. #define Rson mid+1,R,root<<1|1  
  8. int n,sum[MAX<<2];  
  9. int Pushup(int root) //把当前结点的信息更新到父节点   
  10. {   sum[root]=sum[root<<1]+sum[root<<1|1];  
  11. }  
  12. void Build(int L,int R,int root)  
  13. {   if(L==R)   
  14.     {   scanf("%d",&sum[root]);  
  15.         return ;  
  16.     }  
  17.     int mid=(L+R)>>1;  
  18.     Build(Lson); //左孩子   
  19.     Build(Rson); //右孩子   
  20.     Pushup(root);   
  21. }  
  22. void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val   
  23. {   if(L==R)  
  24.     {   sum[root]+=val;  
  25.         return ;  
  26.     }   
  27.     int mid=(L+R)>>1;  
  28.     if(q<=mid) Update(q,val,Lson); //说明p在左结点   
  29.     else Update(q,val,Rson); //说明p在右结点   
  30.     Pushup(root);  
  31. }  
  32. int Query(int ql,int qr,int L,int R,int root) //在根为root,区间为[L,R]的线段树中,计算区间[ql,qr]的和    
  33. {   if(ql<=L && R<=qr) return sum[root];  
  34.     int mid=(L+R)>>1; //利用二分的思想   
  35.     int res=0;  
  36.     if(ql<=mid) res+=Query(ql,qr,Lson);  
  37.     if(qr>mid) res+=Query(ql,qr,Rson);  
  38.     return res;  
  39. }  
  40. int main()  
  41. {   int a,b,Case,num=1;  
  42.     scanf("%d",&Case);  
  43.     while(Case--)  
  44.     {   printf("Case %d:\n",num++);  
  45.         scanf("%d",&n);  
  46.         Build(1,n,1);     
  47.         char op[10];  
  48.         while(scanf("%s",op))  
  49.         {   if(op[0]=='E'break;  
  50.             scanf("%d%d",&a,&b);  
  51.             if(op[0]=='A') Update(a,b,1,n,1);  
  52.             if(op[0]=='S') Update(a,-b,1,n,1);  
  53.             if(op[0]=='Q') printf("%d\n",Query(a,b,1,n,1));  
  54.         }  
  55.     }   
  56.     return 0;  
  57. }  

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值