【模板】线段树+懒操作

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例#1: 
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1: 
11
8
20

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^,保证在int64/long long数据范围内)

 

线段树有区间修改及求和的功能。其中区间修改包括单点修改和成段替换。当数据比较大的时候,有时需要用到懒操作来降低时间复杂度。

重要步骤在代码里都有注释:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 #define LL long long
 8 #define lson l,m,rt<<1   //这里一定要注意:不是1(数字)~m而是l(字母)~m!!
 9 #define rson m+1,r,rt<<1|1  //同上,不是n而是r!
10 using namespace std;
11 const int MAXN=100000;
12 int n,m;
13 LL sum[MAXN<<2],pl[MAXN<<2];
14 void pushup(int rt)
15 {
16     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
17 }
18 void build(int l,int r,int rt)
19 {
20     pl[rt]=0;
21     if(l==r)
22     {
23         scanf("%lld",&sum[rt]);
24         return ;
25     }
26     int m=(l+r)>>1;
27     build(lson);
28     build(rson);
29     pushup(rt);
30 }
31 void pushdown(int rt,int len)  //懒操作:只记录当前一步需要更新的数值
32 {
33     if(pl[rt])  //如果当前有之前标记的数值
34     {
35         pl[rt<<1]+=pl[rt];  //父区间更新,他的左右子区间 也会更新
36         pl[rt<<1|1]+=pl[rt];
37         sum[rt<<1]+=pl[rt]*(len-(len>>1));  //左子树更新了几个
38         sum[rt<<1|1]+=pl[rt]*(len>>1);
39         pl[rt]=0;//这里一定要记得赋零
40     }
41 }
42 void update(int L,int R,int add,int l,int r,int rt)
43 {
44     if(L<=l && r<=R)
45     {
46         pl[rt]+=add;  //懒标记
47         sum[rt]+=(LL)add*(r-l+1);
48         return ;
49     }
50     int m=(l+r)>>1;
51     pushdown(rt,r-l+1);
52     if(L<=m) update(L,R,add,lson);
53     if(m+1<=R) update(L,R,add,rson);
54     pushup(rt);    
55 }
56 LL query(int L,int R,int l,int r,int rt)  
57 {
58     if(L<=l && r<=R) return sum[rt];
59         pushdown(rt,r-l+1);   //检查有没有上一步留下的懒标记
60     int m=(l+r)>>1;
61     LL ret=0;
62     if(L<=m) ret+=query(L,R,lson);
63     if(R>m) ret+=query(L,R,rson);
64     return ret;
65 }
66 
67 int main()
68 {
69     scanf("%d%d",&n,&m);
70     build(1,n,1);
71     for(int i=1;i<=m;i++)
72     {
73         int c;
74         scanf("%d",&c);
75         if(c==1)
76         {
77             int x,y,k;
78             scanf("%d%d%d",&x,&y,&k);
79             update(x,y,k,1,n,1);
80         }
81         else
82         {
83             int x0,y0;
84             scanf("%d%d",&x0,&y0);
85             printf("%lld\n",query(x0,y0,1,n,1));
86         }
87     }
88     return 0;
89 }
Segment tree(1)

本题用到了成段替换及求和的操作。

-----------------------------------------------------12.2补---------------------------------------------------

在码模板的时候有以下几点要特别注意:
1.‘<<’ 还是 '>>':'<<'是左移,也就是把原来的数*2; '>>'是右移,/2。另外还要注意符号后面的数字,<<1就是*2,而不是<<2

2.pushdown里注意都是+=。另外update和query里面都需要pushdown(如果有懒操作的话)

3.只有build里面是l==r,update和query里面都是(L<=l && r<=R)

 

转载于:https://www.cnblogs.com/lulala/p/7894605.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值