线段树+lazy标记
- 线段树基于分治思想的二叉树,用于区间信息统计
特点:
- 每个节点代表区间
- 唯一根节点,也就是全部区间
- 叶节点是长度为1的子区间,也就是所代表数组上的一个点
- 存线段树的结构体数组长度一般是 线段树所代表数组长度的4倍.
1
2
3
4
5const int maxn = 1e3;
struct segmentTree {
int l,r;
int dat;
} e[4 * maxn]; - 线段树3个操作
- 建树
1
2
3
4
5
6
7
8
9
10
11void build(int p,int l,int r) {
e[p].l = l; e[p].r = r;
if (l == r) {
e[p].dat = a[l]; // 存原数组的值
return ;
}
int mid = (l+r)/2;
build(p*2,l,mid); //递归建左子树
build(p*2+1,mid+1,r); //右子树
e[p].dat = max(e[p*2].dat,e[p*2+1].dat); //从下到上(回溯)更新区间节点的最大值
} - 修改某节点值
1
2
3
4
5
6
7
8
9
10void change(int p,int index,int val) {
if (e[p].l == e[p].r) {
e[p].dat = val;
return ;
}
int mid = (e[p].l + e[p].r)/2;
if (index <= mid) change(p*2,index,val);
else change(p*2+1,index,val);
e[p].dat = max(e[p*2].dat,e[p*2+1].dat);
} - 询问区间最值
1 |
|
线段树例题
- 此线段树存的是区间和,不是区间最值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn];
int n;
struct segmentTree {
int l,r;
int dat;
} e[4 * maxn];
void build(int p,int l,int r) {
e[p].l = l; e[p].r = r;
if (l == r) {
e[p].dat = a[l]; // 存原数组的值
return ;
}
int mid = (l+r)/2;
build(p*2 , l , mid); //递归建左子树
build(p*2+1,mid+1,r); //右子树
e[p].dat = e[p*2].dat + e[p*2+1].dat; //从下到上(回溯)更新区间节点的最大值
}
void change(int p,int index,int val) {
if (e[p].l == e[p].r) {
e[p].dat += val;
return ;
}
int mid = (e[p].l + e[p].r)/2;
if (index <= mid) change(p*2,index,val);
else change(p*2+1,index,val);
e[p].dat = e[p*2].dat + e[p*2+1].dat;
}
int ask(int p,int l,int r) {
if (l <= e[p].l && e[p].r <= r) return e[p].dat;
int mid = (e[p].l + e[p].r)/2;
int val = 0;
if (l <= mid) val += ask(p*2 ,l,r); // l,r 代表询问的区间
if (r > mid) val += ask(p*2+1,l,r);
return val;
}
int main() {
// freopen("a.txt","r",stdin);
int cnt=0;
int times; scanf("%d",×);
while (times--) {
scanf("%d",&n);
memset(e,0,sizeof e);
memset(a,0,sizeof a);
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
build(1,1,n);
char ch[10];
printf("Case %d:\n",++cnt);
while (scanf("%s",ch) && ch[0]!='E') {
int l,r; scanf("%d%d",&l,&r);
if (ch[0] == 'Q')
printf("%d\n",ask(1,l,r));
else if (ch[0] == 'A')
change(1,l,r);
else if (ch[0] == 'S')
change(1,l,-r);
}
}
return 0;
}
- 2019/12/15 17:18:15
- 上面线段树写的这么挫,难怪线段树像白学了一样.一直手写不出来,这次重新写一下
- 区间修改+区间查询:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 4e5+10;
int a[maxn/4],left_[maxn],right_[maxn];
ll add[maxn],sum[maxn];
void pushup(int p) { sum[p] = sum[p<<1] + sum[p<<1|1]; }
void pushdown(int p) {
if (add[p]) {
sum[p<<1] += add[p]*(right_[p<<1] - left_[p<<1] + 1);
sum[p<<1|1] += add[p]*(right_[p<<1|1] - left_[p<<1|1] + 1);
add[p<<1] += add[p];
add[p<<1|1] += add[p];
add[p] = 0;
}
}
void build(int p,int l,int r) {
left_[p]=l,right_[p]=r;
if (l == r) {
sum[p] = a[l];
return ;
}
int mid = (l + r) >> 1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int val) {
if (l<=left_[p] && right_[p]<=r) {
sum[p] += (ll)val * (right_[p] - left_[p] + 1);
add[p] += val;
return ;
}
// 要向下继续找,则下传标记
pushdown(p);
int mid = (left_[p] + right_[p]) >> 1;
if (l <= mid)
change(p<<1,l,r,val);
if (mid < r)
change(p<<1|1,l,r,val);
pushup(p);
}
ll query(int p,int l,int r) {
if (l<=left_[p] && right_[p]<=r)
return sum[p];
pushdown(p);
ll ans = 0;
int mid = (left_[p] + right_[p]) >> 1;
if (l <= mid)
ans += query(p<<1,l,r);
if (mid < r)
ans += query(p<<1|1,l,r);
return ans;
}
int n,m;
int main() {
[](){
::ios::sync_with_stdio(false);
::cin.tie(0);
::cout.tie(0);
}();
cin >> n >> m;
for (int i=1; i<=n; ++i)
cin >> a[i];
build(1,1,n);
while(m--) {
int index,x,y,k;
cin >> index;
if (index == 1) {
cin >> x >> y >> k;
change(1,x,y,k);
} else {
cin >> x >> y;
cout << query(1,x,y) << "\n";
}
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!