线段树--线段树【模板1】P3372

题目描述

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

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的结果。

 

对于线段树中的每一个非 叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。
我们就可以得出 节点i的权值=她的左儿子权值+她的右儿子权值。一颗二叉树, 她的左儿子和右儿子编号分别是她*2和她*2+1
1.构建树
 1 inline void add(int x, int l, int r, ll y)
 2 { 
 3     c[x] += (r - l + 1) * y;
 4     lazy[x] += y; 
 5 } 
 6 inline void pushup(int x)
 7 { 
 8     c[x] = c[x*2] + c[x*2+1]; 
 9 }
10 inline void pushdown(int x, int l, int r)
11 { 
12     add(x*2, l, (l+r)/2, lazy[x]);
13     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
14     lazy[x] = 0; 
15 }
16 inline void build(int x, int l, int r)
17 {
18     if(l == r) 
19     return c[x] = a[l], void();
20     build(x*2, l, (l+r)/2);
21     build(x*2+1, (l+r)/2 + 1, r);
22     pushup(x);
23 }

2.单点修改

 1 inline void revise(int x, int l, int r, int l1, int r1, ll y){
 2     if(l1 <= l && r <= r1) 
 3         return add(x, l, r, y); 
 4     if(lazy[x]) 
 5         pushdown(x, l, r);
 6     if(l1 <= (l+r)/2) 
 7         revise(x*2, l, (l+r)/2, l1, r1, y);
 8     if(r1 > (l+r)/2)
 9         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
10     pushup(x);
11 }

3.区间查询

1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值

2、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

3、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

 1 inline ll query(int x, int l, int r, int l1, int r1){
 2     if(l1 <= l && r <= r1) 
 3         return c[x]; 
 4     if(lazy[x]) 
 5         pushdown(x, l, r);
 6     ll sum = 0;
 7     if(l1 <= (l+r)/2) 
 8         sum += query(x*2, l, (l+r)/2, l1, r1);
 9     if(r1 > (l+r)/2) 
10         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
11     return sum; 
12 }

这道题的完整代码:

 1 #include <algorithm>
 2 #include <cmath>
 3 #include <cstdio>
 4 #include <iostream>
 5 #include <cstring>
 6 #define ll long long
 7 using namespace std;
 8 const int N = 500000;
 9 int n, m, l, r, ans;
10 ll k, a[N], c[N], lazy[N]; 
11 inline void add(int x, int l, int r, ll y)
12 { 
13     c[x] += (r - l + 1) * y;
14     lazy[x] += y; 
15 } 
16 inline void pushup(int x)
17 { 
18     c[x] = c[x*2] + c[x*2+1]; 
19 }
20 inline void pushdown(int x, int l, int r)
21 { 
22     add(x*2, l, (l+r)/2, lazy[x]);
23     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
24     lazy[x] = 0; 
25 }
26 inline void build(int x, int l, int r)
27 {
28     if(l == r) 
29     return c[x] = a[l], void();
30     build(x*2, l, (l+r)/2);
31     build(x*2+1, (l+r)/2 + 1, r);
32     pushup(x);
33 }
34 inline ll query(int x, int l, int r, int l1, int r1){
35     if(l1 <= l && r <= r1) 
36         return c[x]; 
37     if(lazy[x]) 
38         pushdown(x, l, r);
39     ll sum = 0;
40     if(l1 <= (l+r)/2) 
41         sum += query(x*2, l, (l+r)/2, l1, r1);
42     if(r1 > (l+r)/2) 
43         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
44     return sum; 
45 }
46 inline void revise(int x, int l, int r, int l1, int r1, ll y){
47     if(l1 <= l && r <= r1) 
48         return add(x, l, r, y); 
49     if(lazy[x]) 
50         pushdown(x, l, r);
51     if(l1 <= (l+r)/2) 
52         revise(x*2, l, (l+r)/2, l1, r1, y);
53     if(r1 > (l+r)/2)
54         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
55     pushup(x);
56 }
57 
58 int main(){
59     scanf("%d %d", &n, &m); 
60     for (int i = 1;i <= n;i++) 
61         scanf("%lld", &a[i]);
62     build(1, 1, n);
63     for (int i = 1;i <= m;i++){
64         scanf("%d %d %d", &ans, &l, &r);
65         if(ans & 1) 
66             scanf("%lld", &k), revise(1, 1, n, l, r, k);
67         else
68             printf("%lld\n", query(1, 1, n, l, r));
69     }
70     return 0;
71 }

最后,还要感谢yxl的耐心指导,whx还需要再内化一段时间的线段树哇

猜你喜欢

转载自www.cnblogs.com/very-beginning/p/12242568.html