线段树--线段树的单点修改,区间修改,区间最值,区间求和
题目描述
如题,已知一个数列,你需要进行下面两种操作:
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的结果。
1.构建树
更新$a_i$的值时,需要对所有包含$i$这个位置的结点的值重新计算。
对于线段树中的每一个非叶子节点$[a,b]$,它的左儿子表示的区间为$[a,(a+b)/2]$,右儿子表示的区间为$[(a+b)/2+1,b]$。我们就可以得出节点i的权值=她的左儿子权值+她的右儿子权值。一颗二叉树,她的左儿子和右儿子编号分别是她*2和她*2+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、如果这个区间被完全包括在目标区间里面,直接修改整个区间
2、如果这个区间的左儿子和目标区间有交集,那么修改左儿子
3、如果这个区间的右儿子和目标区间有交集,那么修改右儿子
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 }
*接下来附上几个基本代码:
一、区间最值
1.单点替换:
1 const int M=100001;
2 LL a[M];
3 LL MAX[M<<2];
4 #define lson l,m,rt<<1
5 #define rson m+1,r,rt<<1|1
6 void update(int rt){
7 MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
8 }
9 void build(int l,int r,int rt){
10 if (l==r) {
11 MAX[rt]=a[l];
12 return;
13 }
14 int m=(l+r)>>1;
15 build(lson);
16 build(rson);
17 update(rt);
18 }
19 void modify(int l,int r,int rt,int p,int v){
20 if (l==r) {
21 MAX[rt]=v;
22 return;
23 }
24 int m=(l+r)>>1;
25 if (p<=m) modify(lson,p,v);
26 else modify(rson,p,v);
27 update(rt);
28 }
29
30 int query(int l,int r,int rt,int nowl,int nowr) {
31 if (nowl<=l && r<=nowr) return MAX[rt];
32 int ans=INT_MIN;
33 int m=(l+r)>>1;
34 if (nowl<=m) ans=max(ans,query(lson,nowl,nowr));
35 if (m<nowr) ans=max(ans,query(rson,nowl,nowr));
36 return ans;
37 }
二、区间和
区间和是线段树可维护的最基本的信息,其他所有线段树可维护的序列信息,都是以区间和为模板建立的。
1.单点增减、单点替换:
1 #define lson l,m,rt<<1
2 #define rson m+1,r,rt<<1|1
3 void update(int rt){
4 sum[rt]=sum[rt<<1]+sum[rt<<1|1];//sum[rt]表示rt节点所包含的区间信息,此处为区间和
5 }
6 void build(int l,int r,int rt){ //构造线段树
7 if (l==r) {
8 sum[rt]=a[l];
9 return;
10 }
11 int m=(l+r)>>1;
12 build(lson);
13 build(rson);
14 update(rt);
15 }
16 void modify(int l,int r,int rt,int p,LL v){ //将p位置修改为v
17 if (l==r) {
18 sum[rt]=v;//如果是将p位置的数+v,则此句应为sum[rt]+=v;
19 return;
20 }
21 int m=(l+r)>>1;
22 if (p<=m) modify(lson,p,v);
23 else modify(rson,p,v);
24 update(rt);
25 }
26
27 LL query(int l,int r,int rt,int nowl,int nowr) { //询问[nowl,nowr]的信息
28 if (nowl<=l && r<=nowr) return sum[rt];
29 LL ans=0;
30 int m=(l+r)>>1;
31 if (nowl<=m) ans+=query(lson,nowl,nowr);
32 if (m<nowr) ans+=query(rson,nowl,nowr);
33 return ans;
34 }
2.区间增减、区间替换:(测试:洛谷P3372)
1 const int M=100001;
2 LL a[M];
3 LL sum[M<<2],lazy[M<<2];
4 #define lson l,m,rt<<1
5 #define rson m+1,r,rt<<1|1
6 void update(int rt){
7 sum[rt]=sum[rt<<1]+sum[rt<<1|1];
8 }
9 void build(int l,int r,int rt){
10 lazy[rt]=0;//懒标记
11 if (l==r) {
12 sum[rt]=a[l];
13 return;
14 }
15 int m=(l+r)>>1;
16 build(lson);
17 build(rson);
18 update(rt);
19 }
20 void clean(int rt,int len){//懒标记下移
21 if(lazy[rt]){
22 lazy[rt<<1]+=lazy[rt];//若为区间替换则为“=”
23 lazy[rt<<1|1]+=lazy[rt];//同上
24 sum[rt<<1]+=lazy[rt]*(len-(len>>1));//同上
25 sum[rt<<1|1]+=lazy[rt]*(len>>1);//同上
26 lazy[rt]=0;
27 }
28 }
29 void modify(int l,int r,int rt,int nowl,int nowr,LL v){
30 int len=r-l+1;
31 if (nowl<=l&&r<=nowr) {
32 lazy[rt]+=v;//若为区间替换则为“=”
33 sum[rt]+=v*len;//同上
34 return;
35 }
36 clean(rt,len);//每次分治前需要下移懒标记,不分治就不下移
37 int m=(l+r)>>1;
38 if(nowl<=m)modify(lson,nowl,nowr,v);
39 if(m<nowr)modify(rson,nowl,nowr,v);
40 update(rt);
41 }
42 LL query(int l,int r,int rt,int nowl,int nowr) {
43 if (nowl<=l && r<=nowr) return sum[rt];
44 clean(rt,r-l+1);//同上
45 LL ans=0;
46 int m=(l+r)>>1;
47 if (nowl<=m) ans+=query(lson,nowl,nowr);
48 if (m<nowr) ans+=query(rson,nowl,nowr);
49 return ans;
50 }
3.区间加减乘混合(测试:洛谷P3373)
1 typedef long long LL;
2 const int M=100001;
3 LL a[M];
4 LL sum[M<<2],add[M<<2],c[M<<2];
5 LL p;//模数
6 #define lson l,m,rt<<1
7 #define rson m+1,r,rt<<1|1
8 void update(int rt){
9 sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%p;
10 }
11 void build(int l,int r,int rt){
12 add[rt]=0;c[rt]=1;//加法懒标记和乘法懒标记
13 if (l==r) {
14 sum[rt]=a[l];
15 return;
16 }
17 int m=(l+r)>>1;
18 build(lson);
19 build(rson);
20 update(rt);
21 }
22 void clean(int rt,int len){
23 if(c[rt]!=1){//先下移乘法标记,再下移加法标记
24 c[rt<<1]=c[rt<<1]*c[rt]%p;
25 c[rt<<1|1]=c[rt<<1|1]*c[rt]%p;
26 add[rt<<1]=add[rt<<1]*c[rt]%p;//加法标记也要乘上乘数
27 add[rt<<1|1]=add[rt<<1|1]*c[rt]%p;
28 sum[rt<<1]=sum[rt<<1]*c[rt]%p;
29 sum[rt<<1|1]=sum[rt<<1|1]*c[rt]%p;
30 c[rt]=1;
31 }
32 if(add[rt]){
33 add[rt<<1]=(add[rt<<1]+add[rt])%p;
34 add[rt<<1|1]=(add[rt<<1|1]+add[rt])%p;
35 sum[rt<<1]+=add[rt]*(len-(len>>1));sum[rt<<1]%=p;
36 sum[rt<<1|1]+=add[rt]*(len>>1);sum[rt<<1|1]%=p;
37 add[rt]=0;
38 }
39 }
40 void modify(int l,int r,int rt,int nowl,int nowr,LL v){//区间加法
41 int len=r-l+1;
42 if (nowl<=l&&r<=nowr) {
43 add[rt]=(add[rt]+v)%p;
44 sum[rt]=(sum[rt]+v*len%p)%p;
45 return;
46 }
47 clean(rt,len);
48 int m=(l+r)>>1;
49 if(nowl<=m)modify(lson,nowl,nowr,v);
50 if(m<nowr)modify(rson,nowl,nowr,v);
51 update(rt);
52 }
53 void modify_(int l,int r,int rt,int nowl,int nowr,LL v){//区间乘法
54 int len=r-l+1;
55 if(nowl<=l&&r<=nowr){
56 c[rt]=c[rt]*v%p;
57 add[rt]=add[rt]*v%p;
58 sum[rt]=sum[rt]*v%p;
59 return;
60 }
61 clean(rt,len);
62 int m=(l+r)>>1;
63 if(nowl<=m)modify_(lson,nowl,nowr,v);
64 if(m<nowr)modify_(rson,nowl,nowr,v);
65 update(rt);
66 }
67 LL query(int l,int r,int rt,int nowl,int nowr) {
68 if (nowl<=l && r<=nowr) return sum[rt];
69 clean(rt,r-l+1);
70 LL ans=0;
71 int m=(l+r)>>1;
72 if (nowl<=m) ans=(ans+query(lson,nowl,nowr))%p;
73 if (m<nowr) ans=(ans+query(rson,nowl,nowr))%p;
74 return ans;
75 }