线段树
概念线段树的作用线段树的储存解题步骤定义由子节点的信息来计算父节点的信息:pushup()由子节点的信息来计算父节点的信息:pushdown()pushdown 待更新 ……建树:build()查询:query()单点修改 modify()区间修改 (待更新……)
模板题 (HDU 1166)AC代码
可供练习 总题单 week 3 [kuangbin带你飞] 题单 最小生成树 + 线段树 Click here ~~ https://blog.csdn.net/m0_46272108/article/details/108980362
概念
线段树的概念:线段树的本质是一种二叉树。 将每个区间
[
L
,
R
]
[L, R]
[L,R]分解成
[
L
,
M
i
d
]
[L, Mid]
[L,Mid] 和
[
M
i
d
+
1
,
R
]
[ Mid + 1, R]
[Mid+1,R] (其中Mid =
[
L
+
R
2
]
[\frac{L+R}{2}]
[2L+R])这里的
[
x
]
[x]
[x] 表示不大于X的整数,【即向下取整】一直到 左儿子 == 右儿子
假设线段树的开始区间为[1, n] 那么线段树最大高度为 :
l
o
g
2
(
n
−
1
)
+
2
log_2(n-1)+2
log2(n−1)+2 即时间复杂度为:
O
(
l
o
g
n
)
O(logn)
O(logn)
线段树的作用
对于区间(或者线段)的修改、维护,从
O
(
n
)
O(n)
O(n) 的时间复杂度变成
O
(
l
o
g
n
)
O(logn)
O(logn)。
线段树的储存
因为线段树是一棵二叉树,并且除了最后一层,是一颗满二叉树,且,堆也是除了最后一层是一棵满二叉树, 所以一般情况下:用堆(一维数组)来存满整棵树。 存树的时候:
用一维数组存线段树需要开4n的空间:
如果开始的区间长度为n,那么最终叶节点一定为n个(倒数第二层最多有n个点)整棵树(除了最后一层)有2n-1个点(倒数第二层最多有n个点时)最后一层是倒数第二层的两倍,则最多有2n个点最坏的情况下,整棵树有4n-1个结点,那么我们存线段树的时候,要开4倍空间,即如果区间开始长度为n,那么存树的这个数组就要开4n的空间。
解题步骤
线段树日常操作:
定义
const int N
= 50010;
int num
[N
<< 2];
struct Node
{
int l
, r
;
int val
;
}tr
[N
<< 2];
由子节点的信息来计算父节点的信息:pushup()
void pushup(int cur
){
tr
[cur
].val
= tr
[cur
<< 1].val
+ tr
[cur
<< 1 | 1].val
;
}
由子节点的信息来计算父节点的信息:pushdown()
pushdown 待更新 ……
建树:build()
void build(int cur
, int l
, int r
){
tr
[cur
].l
= l
, tr
[cur
].r
= r
, tr
[cur
].val
= 0;
if(l
== r
) {
tr
[cur
].val
= num
[l
];
return;
}
int mid
= l
+ r
>> 1;
build(cur
<< 1, l
, mid
);
build(cur
<< 1 | 1, mid
+ 1, r
);
pushup(cur
);
}
查询:query()
int query(int cur
, int ql
, int qr
) {
int l
= tr
[cur
].l
, r
= tr
[cur
].r
;
if(ql
<= l
&& qr
>= r
)
return tr
[cur
].val
;
int mid
= l
+ r
>> 1;
int val
= 0;
if (ql
<= mid
) {
val
+= query(cur
<< 1, ql
, qr
);
}
if (qr
> mid
) {
val
+= query(cur
<< 1 | 1, ql
, qr
);
}
return val
;
}
单点修改 modify()
modify() 跟build()差不多,一直找叶节点,找到之后,修改
void modify(int cur
, int tar
, int val
) {
int l
= tr
[cur
].l
, r
= tr
[cur
].r
;
if (l
== r
) {
tr
[cur
].val
= tr
[cur
].val
+ val
;
return;
}
int mid
= l
+ r
>> 1;
if (tar
<= mid
) {
modify
(cur
<< 1, tar
, val
);
} else {
modify
(cur
<< 1 | 1, tar
, val
);
}
pushup(cur
);
}
区间修改 (待更新……)
模板题 (HDU 1166)
一个人养水仙花,每盆水仙花都有一个价值,水仙花是排成一行。有三个操作:有时某盆水仙花的价值会上升,有时某盆水仙花的价值会下降。有时他想知道某段连续的水仙花的价值之和是多少,你能快速地告诉她结果吗?
输入格式 第一行一个整数
T
T
T,表示有
T
T
T 组测试数据。 每组测试数据的第一行为一个正整数
N
N
N
(
N
<
=
50000
)
(N<=50000)
(N<=50000),表示有N盆水仙花。 接下来有
N
N
N 个正整数,第
i
i
i 个正整数
a
i
a_i
ai
(
1
<
=
a
i
<
=
50
)
(1<=ai<=50)
(1<=ai<=50) 表示第i盆水仙花的初始价值。 接下来每行有一条命令,命令有4种形式: (1)
A
d
d
Add
Add
i
i
i
j
j
j,
i
i
i 和
j
j
j 为正整数,表示第
i
i
i盆水仙花价值增加
j
(
j
<
=
30
)
j (j<=30)
j(j<=30) (2)
S
u
b
Sub
Sub
i
i
i
j
j
j,
i
i
i 和
j
j
j 为正整数,表示第
i
i
i 盆水仙花价值减少
j
(
j
<
=
30
)
j (j<=30)
j(j<=30) (3)
Q
u
e
r
y
Query
Query
i
i
i
j
j
j,
i
i
i 和
j
j
j 为正整数,
i
<
=
j
i<=j
i<=j,表示询问第
i
i
i 盆水仙花到第
j
j
j 盆水仙花的价值之和 (4)
E
n
d
End
End,表示结束,这条命令在每组数据最后出现 每组数据的命令不超过40000条
输出格式 对于第
i
i
i 组数据,首先输出"Case i:"和回车。 对于每个"Query i j"命令,输出第
i
i
i盆水仙花到第
j
j
j 盆水仙花的美观值之和。 Sample Input
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
Sample Output
Case 1:
6
33
59
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<algorithm>
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std
;
int read()
{
int w
= 1, s
= 0;
char ch
= getchar();
while (ch
< '0' || ch
>'9') { if (ch
== '-') w
= -1; ch
= getchar(); }
while (ch
>= '0' && ch
<= '9') { s
= s
* 10 + ch
- '0'; ch
= getchar(); }
return s
* w
;
}int gcd(int x
,int y
) {
if(x
<y
) swap(x
,y
);
return x
% y
? gcd(y
, x
% y
) : y
;
}
int lcm(int x
,int y
)
{
return x
* y
/ gcd(x
, y
);
}
const int N
= 50010;
int num
[N
* 4];
struct Node
{
int l
, r
;
int val
;
}tr
[N
* 4];
void pushup(int cur
){
tr
[cur
].val
= tr
[cur
<< 1].val
+ tr
[cur
<< 1 | 1].val
;
}
void build(int cur
, int l
, int r
){
tr
[cur
].l
= l
, tr
[cur
].r
= r
, tr
[cur
].val
= 0;
if(l
== r
) {
tr
[cur
].val
= num
[l
];
return;
}
int mid
= l
+ r
>> 1;
build(cur
<< 1, l
, mid
);
build(cur
<< 1 | 1, mid
+ 1, r
);
pushup(cur
);
}
int query(int cur
, int ql
, int qr
) {
int l
= tr
[cur
].l
, r
= tr
[cur
].r
;
if(ql
<= l
&& qr
>= r
)
return tr
[cur
].val
;
int mid
= l
+ r
>> 1;
int val
= 0;
if (ql
<= mid
) {
val
+= query(cur
<< 1, ql
, qr
);
}
if (qr
> mid
) {
val
+= query(cur
<< 1 | 1, ql
, qr
);
}
return val
;
}
void modify(int cur
, int tar
, int val
) {
int l
= tr
[cur
].l
, r
= tr
[cur
].r
;
if (l
== r
) {
tr
[cur
].val
= tr
[cur
].val
+ val
;
return;
}
int mid
= l
+ r
>> 1;
if (tar
<= mid
) {
modify
(cur
<< 1, tar
, val
);
} else {
modify
(cur
<< 1 | 1, tar
, val
);
}
pushup(cur
);
}
int main()
{
int t
= read();
for(int i
= 1; i
<= t
; i
++)
{
int n
= read();
for(int j
= 1; j
<= n
; j
++)
num
[j
] = read();
build(1, 1, n
);
int flag
= 1;
while (1) {
string str
;
cin
>> str
;
if (flag
) {
printf("Case %d:\n", i
);
flag
= 0;
}
if (str
== "Query") {
int ql
= read(), qr
= read();
printf("%d\n", query(1, ql
, qr
));
}
if (str
== "Add") {
int c
= read(), m
= read();
modify(1, c
, m
);
}
if (str
== "Sub") {
int c
= read(), m
= read();
modify(1, c
, -m
);
}
if (str
== "End")
break;
}
}
return 0;
}