题目描述
如题,已知一个数列,你需要进行下面两种操作:
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还需要再内化一段时间的线段树哇
原文地址:https://www.cnblogs.com/very-beginning/p/12242568.html
时间: 2024-10-29 06:04:02