题解 P3372 [线段树 1]
题目描述
如题,已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上 k。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:
1 x y k:将区间 [x,y] 内每个数加上 k。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
说明/提示
对于 30% 的数据:n ≤ 8, m ≤ 10。
对于 70% 的数据:n ≤ 103, m ≤ 104。
对于 100% 的数据:1 ≤ n, m ≤ 105。
保证任意时刻数列中任意元素的和在 [-263, 263)内
【样例解释】

code:
1 #include <bits/stdc++.h> 2 #define REP(i, a, b) for (long long i = a; i <= b; ++i) 3 #define ll long long 4 #define N 100010 5 using namespace std; 6 7 ll n, m, a[N]; 8 9 struct xcj{ 10 ll l, r, value, add; 11 #define l(x) t[x].l 12 #define r(x) t[x].r 13 #define sum(x) t[x].value 14 #define add(x) t[x].add 15 } t[N * 4]; 16 17 inline ll read(){ //快读 18 ll s = 0, w = 1; 19 char ch = getchar(); 20 while (ch < '0' || ch > '9'){ 21 if (ch == '-') w *= -1; 22 ch = getchar(); 23 } 24 while (ch >= '0' && ch <= '9'){ 25 s = s * 10 + ch - '0'; 26 ch = getchar(); 27 } 28 return s * w; 29 } 30 31 inline void spread(ll p){ 32 if (add(p)){ //是否有延迟标记 33 sum(p * 2) += add(p) * (r(p * 2) - l(p * 2) + 1); //更新左儿子的值 34 sum(p * 2 + 1) += add(p) * (r(p * 2 + 1) - l(p * 2 + 1) + 1); //更新右儿子的值 35 add(p * 2) += add(p); //增加左儿子的延迟标记 36 add(p * 2 + 1) += add(p); //增加右儿子的延迟标记 37 add(p) = 0; //清除p节点的延迟标记 38 } 39 } 40 41 inline void change(ll p, ll l, ll r, ll v){ 42 if (l <= l(p) && r >= r(p)){ 43 sum(p) += v * (r(p) - l(p) + 1); //加上整棵子树的值 44 add(p) += v; //增加延迟标记 45 return ; 46 } 47 spread(p); 48 ll mid = (l(p) + r(p)) / 2; //取中间值 49 if (l <= mid) change(p * 2, l, r, v); //区间的左边界是否在左子树中 50 if (r > mid) change(p * 2 + 1, l, r, v); //区间的右边界是否在右子树中 51 sum(p) = sum(p * 2) + sum(p * 2 + 1); //p节点的值=左儿子的值+右儿子的值 52 } 53 54 inline ll ask(ll p, ll l, ll r){ //查询 55 if (l <= l(p) && r >= r(p)) return sum(p); //如果完全覆盖就直接返回 56 spread(p); 57 ll mid = (l(p) + r(p)) / 2, valu = 0; //取中间值 58 if (l <= mid) valu += ask(p * 2, l, r); //区间的左边界是否在左子树中 59 if (r > mid) valu += ask(p * 2 + 1, l, r); //区间的右边界是否在右子树中 60 return valu; 61 } 62 63 inline void build(ll p, ll l, ll r){ //建树 64 l(p) = l, r(p) = r; //判断是否是叶子节点 65 if (l == r){ 66 sum(p) = a[l]; 67 return ; 68 } 69 ll mid = (l + r) / 2; 70 build(p * 2, l, mid); //建左子树 71 build(p * 2 + 1, mid + 1, r); //建右子树 72 sum(p) = sum(p * 2) + sum(p * 2 + 1); //p节点的值=左儿子的值+右儿子的值 73 } 74 75 inline void init(){ //输入 76 n = read(), m = read(); 77 REP(i, 1, n) a[i] = read(); 78 } 79 80 inline void work(){ 81 init(); 82 build(1, 1, n); 83 while (m--){ 84 ll lyj = read(), l = read(), r = read(); 85 if (lyj == 1){ //如果lyj等于1,就执行区间加操作 86 ll val = read(); 87 change(1, l, r, val); 88 } else printf("%lld\n", ask(1, l, r)); //否则就执行查询操作 89 } 90 } 91 92 int main(){ 93 work(); 94 return 0; 95 }
浙公网安备 33010602011771号