线段树经典操作模板(单点更新,替换;区间更新,替换;区间求和求最值)

对于线段树的讲解此篇不再赘述,下面列出线段树应用中最常用的几种操作的代码。(具体题目未贴出,仅供有一定基础者参考代码风格)

另外,注意多组输入要写scanf("%d%d",&n,&m)!=EOF,线段树的题肯定要用c语言的输入输出,要使用字符数组,不用字符串,输入字符的时候要加getchar()吞噬空行..

(1)单点增减,区间求和:

[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<stdio.h>  
  3. #include<string>  
  4. #include<string.h>  
  5. using namespace std;  
  6. int a[50000*4];//开四倍即可   
  7. int sum[50000*4];  
  8. int T,n,b,c,ans;  
  9. char s[20];  
  10. //单点增减,区间求和  
  11. int build(int l,int r,int o)//l左,r右,o结点   
  12. {  
  13.     if(l==r)//由上至下建树   
  14.     return sum[ o]=a[l];  
  15.     else  
  16.     {  
  17.         int mid=l+(r-l)/2;  
  18.         return sum[o]=build(l,mid,2*o)+build(mid+1,r,2*o+1);   
  19.     }  
  20.       
  21. }  
  22. void query(int l,int r,int o)//b,c是要求和的区间   
  23. {  
  24.     if(b<=l&&c>=r)//注意是b,c包含l,r   
  25.     ans+=sum[o];  
  26.     else  
  27.     {  
  28.         int mid=l+(r-l)/2;  
  29.         if(b<=mid)  
  30.         query(l,mid,o*2);  
  31.         if(c>mid)  
  32.         query(mid+1,r,2*o+1);  
  33.     }   
  34. }  
  35. void add(int l,int r,int o)  
  36. {  
  37.     sum[o]+=c;//注意这个点要加的值   
  38.     if(l==r)  
  39.     return;  
  40.     else  
  41.     {  
  42.       int mid=l+(r-l)/2;  
  43.       if(b<=mid)  
  44.       add(l,mid,o*2);  
  45.       else  
  46.       add(mid+1,r,o*2+1);     
  47.     }  
  48. }  
  49. int main()  
  50. {  
  51.     int ca=1;  
  52.     scanf("%d",&T);  
  53.     while(T--)  
  54.     {  
  55.         scanf("%d",&n);  
  56.         for(int i=1;i<=n;i++)  
  57.         scanf("%d",&a[i]);  
  58.         printf("Case %d:\n",ca++);  
  59.         build(1,n,1);  
  60.         while(scanf("%s",s))  
  61.         {  
  62.             if(s[0]=='E')  
  63.             break;  
  64.             if(s[0]=='A')  
  65.             {  
  66.                 scanf("%d%d",&b,&c);  
  67.                 add(1,n,1);  
  68.             }  
  69.             if(s[0]=='S')  
  70.             {  
  71.                scanf("%d%d",&b,&c);  
  72.                 c=-c;  
  73.                 add(1,n,1);  
  74.             }  
  75.           
  76.             if(s[0]=='Q')  
  77.             {  
  78.                 ans=0;  
  79.                 scanf("%d%d",&b,&c);  
  80.                 query(1,n,1);  
  81.                 printf("%d\n",ans);  
  82.             }  
  83.         }  
  84.     }  
  85.     return 0;  

(2) 单点更新,区间求最值

[cpp]  view plain copy
  1. int build(int l,int r,int o)//l左,r右,o结点   
  2. {  
  3.     if(l==r)//由上至下建树   
  4.     return max1[o]=a[l];//最值建树   
  5.     else  
  6.     {  
  7.         int mid=l+(r-l)/2;  
  8.         return max1[o]=max(build(l,mid,2*o),build(mid+1,r,2*o+1));   
  9.     }  
  10.       
  11. }  
  12. void update(int l,int r,int o)//单点更新   
  13. {  
  14. //  sum[o]+=c;  
  15.     max1[o]=max(max1[o],c);//从原来值和改变值之间取最大的   
  16.     if(l==r)  
  17.     return;  
  18.     else  
  19.     {  
  20.       int mid=l+(r-l)/2;  
  21.       if(b<=mid)  
  22.       update(l,mid,o*2);  
  23.       else  
  24.       update(mid+1,r,o*2+1);      
  25.     }  
  26. }  
  27.   
  28. void query(int l,int r,int o)//b,c是要求最值的区间   
  29. {  
  30.     if(b<=l&&c>=r)//注意是b,c包含l,r   
  31.     ans=max(ans,max1[o]);//取最大的,ans初值为0   
  32.         //ans+=sum[o];  
  33.     else  
  34.     {  
  35.         int mid=l+(r-l)/2;  
  36.         if(b<=mid)  
  37.         query(l,mid,o*2);  
  38.         if(c>mid)  
  39.         query(mid+1,r,2*o+1);  
  40.     }   
  41. }     

(3)区间替换,区间求和

[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<stdio.h>  
  3. using namespace std;  
  4. const int MAX_N=100100;  
  5. int sum[MAX_N<<2],col[MAX_N<<2];  
  6. int n,q,x,y,c;  
  7.   
  8. void push_down(int l,int r,int o)//下放   
  9. {  
  10.     int m=(l+r)>>1;  
  11.     if(col[o])  
  12.     {  
  13.         col[o<<1]=col[o<<1|1]=col[o];  
  14.         sum[o<<1]=(m-l+1)*col[o];  
  15.         sum[o<<1|1]=(r-m)*col[o];  
  16.         col[o]=0;  
  17.     }  
  18. }  
  19. void build(int l,int r,int o)  
  20. {  
  21.     col[o]=0;  
  22.     sum[o]=1;  
  23.     if(l==r)  
  24.     return ;  
  25.     int m=(l+r)>>1;  
  26.     build(l,m,o<<1);  
  27.     build(m+1,r,o<<1|1);  
  28.     sum[o]=sum[o<<1]+sum[o<<1|1];  
  29. }  
  30.   
  31. void update(int l,int r,int o)  
  32. {  
  33.     int m=(l+r)>>1;  
  34.     if(x<=l&&r<=y)  
  35.     {  
  36.         col[o]=c;  
  37.         sum[o]=(r-l+1)*c;  
  38.         return ;  
  39.     }  
  40.     push_down(l,r,o);  
  41.     if(x<=m)  
  42.     update(l,m,o<<1);  
  43.     if(y>m)  
  44.     update(m+1,r,o<<1|1);  
  45.     sum[o]=sum[o<<1]+sum[o<<1|1];  
  46. }  
  47. int main()  
  48. {  
  49.     int T,cas=1;  
  50.     scanf("%d",&T);  
  51.     while(T--)  
  52.     {  
  53.         scanf("%d%d",&n,&q);  
  54.         build(1,n,1);  
  55.         while(q--)  
  56.         {  
  57.            scanf("%d%d%d", &x, &y, &c);  
  58.            update(1,n,1);  
  59.         }  
  60.         printf("Case %d: The total value of the hook is %d.\n", cas++, sum[1]);  
  61.     }  
  62.     return 0;  
  63. }  

(4)区间更新,区间求和

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. using namespace std;  
  4. #define N 100005  
  5. __int64 sum[N << 2];  
  6. __int64 mark[N << 2];  
  7. __int64 basic_num[N<<2];  
  8. inline void pushUp (int o)  
  9. {  
  10.     sum[o] = sum[o << 1] + sum[o << 1 | 1];  
  11. }  
  12.   
  13. void pushDown (int o, int len)  
  14. {  
  15.     if (mark[o]) {  
  16.         //因为o的儿子节点可能被多次延迟标记,并且o的儿子节点的延迟标记没有向o的孙子节点移动,所以用“+=”  
  17.         mark[o << 1] += mark[o];  
  18.         mark[o << 1 | 1] += mark[o];  
  19.         /*此处用mark[o]乘以区间长度,不是mark[o<<1], 因为o的儿子节点如果被多次标记,之前被标记时, 
  20.           就已经对sum[o<<1]更新过了。 
  21.         */  
  22.         sum[o << 1] += mark[o] * (len - (len >> 1));   
  23.         sum[o << 1 | 1] += mark[o] * (len >> 1);  
  24.         mark[o] = 0; //将标记向儿子节点移动后,父节点的延迟标记去掉  
  25.     }  
  26. }  
  27.   
  28. void build (int l, int r, int o)  
  29. {  
  30.     mark[o] = 0;  
  31.     if (l == r)  
  32.     {  
  33.         sum[o]=basic_num[l];  
  34.         return;  
  35.     }  
  36.     int m = (l + r) >> 1;  
  37.     build (l, m, o << 1);  
  38.     build (m + 1, r, o << 1 | 1);  
  39.     pushUp (o);//sum值初始化   
  40. }  
  41.   
  42. void update (int L, int R, __int64 c, int l, int r, int o)//L,R是要更新的区间   
  43. {  
  44.     if (l >= L && r <= R)  
  45.     {  
  46.         mark[o] += c;  
  47.         sum[o] += c * (r - l + 1);  
  48.         return;  
  49.     }  
  50.     /*当要对被延迟标记过的这段区间的儿子节点进行更新时,先要将延迟标记向儿子节点移动 
  51.     当然,如果一直没有对该段的儿子节点更新,延迟标记就不需要向儿子节点移动,这样就使 
  52.     更新操作的时间复杂度仍为O(logn),也是使用延迟标记的原因。 
  53.     */  
  54.     pushDown(o, r - l + 1); //将已有的延迟下放      
  55.     int m = (l + r) >> 1;  
  56.     if (m >= L) update (L, R, c, l, m, o << 1);  
  57.     if (m < R) update (L, R, c, m + 1, r, o << 1 | 1);  
  58.     pushUp (o);  
  59. }  
  60.   
  61. __int64 query (int L, int R, int l, int r, int o)  
  62. {  
  63.     if (l >= L && r <= R)   
  64.     return sum[o];  
  65.     //要取o子节点的值时,也要先把o的延迟标记向下移动  
  66.     pushDown(o, r - l + 1);  
  67.     int m = (l + r) >> 1;  
  68.     __int64 ret = 0;  
  69.     if (m >= L) ret += query (L, R, l, m, o << 1);  
  70.     if (m < R) ret += query (L, R, m + 1, r, o << 1 | 1);  
  71.     return ret;  
  72. }  
  73.   
  74. int main()  
  75. {  
  76.     int n, q, a, b;  
  77.     __int64 c;  
  78.     char op;  
  79.     scanf ("%d%d", &n,&q);  
  80.     for(int i=1;i<=n;i++)  
  81.     scanf("%I64d",&basic_num[i]);  
  82.     build (1, n, 1);  
  83.     while (q--)  
  84.     {  
  85.         getchar();  
  86.         scanf ("%c %d %d", &op, &a, &b);  
  87.         if (op == 'Q') printf ("%I64d\n", query (a, b, 1, n, 1));  
  88.         else  
  89.         {  
  90.             scanf ("%I64d", &c);  
  91.             update (a, b, c, 1, n, 1);  
  92.         }  
  93.     }  
  94.     return 0;  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值