线段树--线段树的单点修改,区间修改,区间最值,区间求和

 

题目描述

如题,已知一个数列,你需要进行下面两种操作:

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
", 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 }

 

原文地址:https://www.cnblogs.com/very-beginning/p/12242568.html