线段树模板

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 }

 

posted @ 2023-06-27 15:45  smiling&weeping  阅读(18)  评论(0)    收藏  举报