本文以codeforces EDU segment tree为资料
content
introdcution
线段树是一种采用了分治和记忆化的数据结构,主要用于区间查询和修改。
对
[
l
,
r
)
[l,r)
[l,r)的区间,它的结果由
[
l
,
m
i
d
)
,
[
m
i
d
,
r
)
[l,mid),[mid,r)
[l,mid),[mid,r)决定,比如
m
i
n
(
[
l
,
r
)
)
=
m
i
n
(
m
i
n
(
[
l
,
m
i
d
)
)
,
m
i
n
(
[
m
i
d
,
r
)
)
)
min([l,r))=min(\ min([l,mid)),\ min([mid,r))\ )
min([l,r))=min( min([l,mid)), min([mid,r)) ),那么我们可以建立一个以最长区间的合适长度(一般为二的幂次)为叶子节点的数量,从根节点自上而下建树,叶子节点的值由外部输入初始化,非叶子节点的值由子节点建立。对于节点
x
x
x,有
l
x
,
r
x
,
v
a
l
u
e
s
lx,rx,values
lx,rx,values,其中
l
x
,
r
x
lx,rx
lx,rx表示节点
x
x
x的范围,
v
a
l
u
e
s
values
values是值。
m
i
d
=
(
l
x
+
r
x
)
/
2
mid=(lx+rx)/2
mid=(lx+rx)/2,那么由节点
x
x
x向下搜索
2
x
+
1
2x+1
2x+1节点
(
l
x
,
m
i
d
,
v
a
l
u
e
s
)
(lx,mid,values)
(lx,mid,values)和
2
x
+
2
2x+2
2x+2节点
(
m
i
d
,
r
x
,
v
a
l
u
e
s
)
(mid,rx,values)
(mid,rx,values),搜索完后更新
x
x
x节点。根节点的范围为
[
0
,
s
i
z
e
)
[0,size)
[0,size),
s
i
z
e
size
size表示最小
⩾
n
\geqslant n
⩾n的二次幂
2
k
2^k
2k,这样我们就完成了建树,建树的范围最大为
2
n
2n
2n,总节点数最大为
4
n
4n
4n
对于查询过程
l
,
r
l,r
l,r,我们仍从根节点向下搜索涉及到的范围。对于
l
x
⩾
r
o
r
r
x
⩽
l
lx \geqslant r\ or\ rx \leqslant l
lx⩾r or rx⩽l,那么该节点
x
x
x的范围不在查询范围内;可以直接退出,对于
l
x
⩾
l
a
n
d
r
x
⩽
r
lx \geqslant l\ and\ rx \leqslant r
lx⩾l and rx⩽r,那么该节点
x
x
x的范围完全在查询范围内,也可以直接退出;剩下的情况是两者有交叉部分:那么我们仍然考虑分治的思想,向下搜索
2
x
+
1
,
2
x
+
2
2x+1,2x+2
2x+1,2x+2节点,查询范围可以不变的向下传递。采用递归的方式可以很好的在叶子节点更新后返回非叶子节点更新。
problems & solutions
step 4
A. Sign alternation
建树,更新,查询时考虑正负即可
B. Cryptography
矩阵乘法,对calc部分进行修改即可
但这题由于输入输出的关系容易超时,fast I/O改进
同时,考虑sparse table,减少建树所需要的点的数量
C. Number of Inversions on Segment
i
n
v
[
l
,
r
)
=
i
n
v
[
l
,
m
i
d
)
+
i
n
v
[
m
i
d
,
r
)
+
n
u
m
(
a
i
>
a
j
)
(
i
∈
[
l
,
m
i
d
)
,
j
∈
[
m
i
d
,
r
)
)
inv[l,r)=inv[l,mid)+inv[mid,r)+num(a_i>a_j)\ (i\in [l,mid),j\in [mid,r)\ )
inv[l,r)=inv[l,mid)+inv[mid,r)+num(ai>aj) (i∈[l,mid),j∈[mid,r) )
n
u
m
(
a
i
>
a
j
)
num(a_i>a_j)
num(ai>aj)可以用颜色桶排解决
由于颜色种类只有40,那么时间复杂度是可以接受的
D. Number of Different on Segment
同样考虑单个颜色,在区间 [ l , r ) [l,r) [l,r)范围是否出现只需要考虑 s u m [ l , r ) sum[l,r) sum[l,r)
E. Earthquakes
对节点的更新仅是单点的,考虑到每次查询区间 ] l , r ) ]l,r) ]l,r)比 x x x小的值时都会将其节点更新为 i n f inf inf,那么以最小值的方法建立线段树,每次查询的均摊复杂度是 n l o g ( N ) + O ( n u m ) n + 1 , ∑ n u m ⩽ N , O ( n u m ) = l o g ( n ) \frac {nlog(N)+O(num)}{n+1},\sum num\leqslant N,O(num)=log(n) n+1nlog(N)+O(num),∑num⩽N,O(num)=log(n), n u m num num表示查询时会修改的节点的数量,由于我们用最小值的方法建树,那么我们最多考虑到2倍的节点,总时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)级别的。