数据分析之线段树入门
前置知识:
- OI中的群论初步
- 线段树
幺半群
对于集合 \(G\) 和二元运算 \(*\),如果满足如下条件,则称二元组 \((G, *)\) 是幺半群:
- \(\forall g_1, g_2\in G, g_1*g_2\text{有定义并且} g_1*g_2\in G\)(封闭性)
- \(\exists e\in G, s.t.\forall g\in G, e*g=g*e=g\)(单位元)
- \(\forall a, b, c\in G, (a*b)*c=a*(b*c)\)(结合律)
容易发现如果一个幺半群另满足逆元存在性,则它是一个群。
引入
考虑幺半群 \((G, +)\),设数列 \(a_1=a_2=......=a_n=e\)(\(e\) 是 \(G\) 中的单位元)。另设 \((F, \circ)\) 是幺半群,其中每个元素均是定义域和值域均为 \(G\) 的函数。要求支持如下操作:
- 给出 \(l, r\),求 \(\sum_{i=l}^ra_i\)。
- 给出 \(l, r, f\in F\),令 \(a_i\leftarrow f(a_i)\) 对一切 \(i\in[l, r]\)
线段树
为了方便起见,定义离散区间 \([l, r]=\{x|x\in \mathbb{Z}, l\leq x\leq r\}\)。为了避免和一般的区间混淆,以后所有的方括号区间都表示离散区间。
考虑一颗线段树,它的根节点是 \([1, n]\)。对于任意节点 \([l, r]\)。如果 \(l\ne r\),那么它的左儿子是 \(ls([l, r])=[l, [\frac{l+r}{2}]]\),右儿子是 \(rs([l, r])=[[\frac{l+r}{2}]+1, r]\)。
为了方便表述,定义 \(s_u\) 是 \(u\) 的子树的区间集合,定义 \(p_u\) 是 \(u\) 的所有祖先的区间集合。
定义区间信息 \(t\) 是线段树上区间到 \(G\) 的映射,定义懒标记 \(l\) 是线段树上区间到 \(F\) 的映射。
根据定义,容易有定理:
定理:对于任意区间 \([l, r]\),存在 \(O(\log n)\) 个线段树中的区间,满足它们两两无交且并为 \([l, r]\)。
定义函数 \(A(F)=(f_1\circ f_2\circ ...... \circ f_k)(e)\),其中 \(\{f_1, f_2, ......., f_k\}=F\) 并且已经按照区间长度从大到小排序。
于是有:
定理:\(a_i=A(l(s_u\cup p_u))(u\in p_{[i, i]})\)。
区间信息 \(t\) 必须满足条件:任何一个操作完成后,\(t([l, r])=\sum_{i=l}^rA(l(p_{[i, i]}\setminus p_u))\)。
修改操作
根据定理,设 \(|S|=O(\log n)\) 并且 \(S\) 是互不相交地构成区间 \([l, r]\) 的线段树节点集合。
设 \(p(S)=\bigcup_{s\in S}p(s)\)。
那么修改操作只需要执行如下两步:
- \(\forall s\in S, l(s)\leftarrow f\circ l(s), t(s)\leftarrow f(t(s))\)。
- \(\forall u\in p(S), t(u)=t(ls(u))+t(rs(u))\)(按照区间长度从小到大依次执行)。
容易归纳证明这两步能够保证区间信息 \(t\) 必须满足的条件。
查询操作
设 \(|S|=O(\log n)\) 并且 \(S\) 是互不相交地构成区间 \([l, r]\) 的线段树节点集合。
查询操作只需执行如下两步:
- \(\forall u\in p(S), l(ls(u))\leftarrow l(u)\circ l(ls(u)), t(ls(u))\leftarrow l(u)(t(ls(u)))(\text{对} rs \text{同理}), l(u)\leftarrow e\)。按区间从大到小执行。(目的是令 \(l(p(S))=\{e\}\))
- 输出 \(\sum_{s\in S}t(s)\)。
为什么 \(\sum_{s\in S}t(s)=\sum_{i=l}^ra_i\)?因为左式等于 \(\sum_{s\in S}\sum_{i=l}^rA(l(p_{[i, i]}/p_u))=\sum_{s\in S}\sum_{i=l}^rA(l(p_{[i, i]}))=\sum_{s\in S}\sum_{i=l}^ra_i\)。
这样,你就掌握了在 \(O(\log n)\) 的时间复杂度内完成修改和查询操作的数据结构-线段树。
查询操作(标记永久化)
使用标记永久化的技巧,只需要一步就能完成查询操作:
定义函数 \(A(F, x)=(f_1\circ f_2\circ ...... \circ f_k)(x)\),其中 \(\{f_1, f_2, ......., f_k\}=F\) 并且已经按照区间长度从大到小排序。
输出 \(\sum_{s\in S}A(l(p_s), t(s))\)。
值的注意的是,这个技巧成立必须要求 \(F\) 对 \(G\) 中元素的作用满足线性性,即 \(f(g_1+g_2)=f(g_1)+f(g_2)\)。
证明比较显然。

浙公网安备 33010602011771号