线段树模板
Smiling & <Weeping>
----6.27 温柔正确的人总是难以生存,因为这个世界既不温柔,也不正确
对于中高级数据结构线段树的模板:支持区间求和以及区间查询
题目描述:
# 【模板】线段树 1
## 题目描述
如题,已知一个数列,你需要进行下面两种操作:
1. 将某区间每一个数加上 $k$。
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
### 样例输入 #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
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 100010; //记录数列的元素从a[1]开始 5 ll n , m; 6 ll a[maxn]; 7 ll tree[maxn<<2]; //tree[i]为第i个节点的值,表示一个线段区间的值,如最值、区间和 8 ll tag[maxn<<2]; // tag[i]为第i个节点的Lazy_Tag,统一记录这个区间的修改 9 ll ls(ll p) { return p << 1; } //定位左儿子 10 ll rs(ll p) { return p << 1|1;} //定位右儿子 11 void push_up(ll p) 12 { 13 tree[p] = tree[ls(p)] + tree[rs(p)]; 14 } 15 void build(ll p , ll pl , ll pr) //建树。p为节点编号,它指向区间[pl , pr] 16 { 17 tag[p] = 0; //Lazy_Tag标记 18 if(pl == pr){tree[p] = a[pl]; return ;} 19 //最底层的叶子,赋值 20 ll mid = (pl + pr) >> 1;//分治;折半 21 build(ls(p),pl,mid); 22 build(rs(p),mid+1,pr); 23 push_up(p); //从下往上传递区间值 24 } 25 void addtag(ll p , ll pl , ll pr , ll d)//给节点p打tag标记,并更新tree 26 { 27 tag[p] += d; //打上tag标记 28 tree[p] += d*(pr-pl+1); //计算新的tree 29 } 30 void push_down(ll p , ll pl , ll pr) //不能覆盖时,把tag传给子树 31 { 32 if(tag[p]) //有tag标记,这是以前做区间修改时留下的 33 { 34 ll mid = (pl+pr) >> 1; 35 addtag(ls(p) , pl , mid , tag[p]); //把tag标记传给左子树 36 addtag(rs(p) , mid+1 , pr , tag[p]); //把tag标记传给右子树 37 tag[p] = 0; //p自己的tag被传走了,归零 38 } 39 } 40 void update(ll L , ll R , ll p , ll pl , ll pr , ll d) //区间修改:[L , R]内每个元素加d 41 { 42 if(L <= pl && pr <= R) //完全覆盖,直接返回这个节点,它的子树不用再深入 43 { 44 addtag(p , pl , pr , d); //给节点p打tag标记,下一次区间修改到p时会用到 45 return; 46 } 47 push_down(p , pl , pr); //如果不能覆盖,把tag传给子树 48 ll mid = (pl + pr)>>1; 49 if(L <= mid) update(L , R , ls(p) , pl , mid , d);//递归左子树 50 if(R > mid) update(L , R , rs(p) , mid+1 , pr , d);//递归右子树 51 push_up(p); //更新 52 } 53 ll query(ll L , ll R , ll p , ll pl , ll pr) //查询区间[L , R],p是当前节点(线段)的编号,[pl,pr]是节点p表示的线段区间 54 { 55 if(L <= pl && pr <= R) return tree[p]; //完全覆盖,直接返回 56 push_down(p , pl , pr); //不能覆盖,递归子树 57 ll res = 0; 58 ll mid = (pl + pr) >> 1; 59 if(L <= mid) res += query(L , R , ls(p) , pl , mid); //左节点有重叠 60 if(R > mid) res += query(L , R , rs(p) , mid+1 , pr); //右节点有重叠 61 return res; 62 } 63 int main() 64 { 65 scanf("%lld%lld",&n,&m); 66 for(ll i = 1; i <= n; i++) 67 scanf("%lld",&a[i]); 68 build(1,1,n); 69 while(m--) 70 { 71 ll q,L,R,d; 72 scanf("%lld",&q); 73 if(q == 1) //区间修改:[L,R]的每个元素加d 74 { 75 scanf("%lld%lld%lld",&L,&R,&d); 76 update(L,R,1,1,n,d); 77 } 78 else 79 { 80 scanf("%lld%lld",&L,&R); 81 printf("%lld\n",query(L,R,1,1,n)); 82 } 83 } 84 return 0; 85 }