线段树小记

好像网上对于那些历史版本(历史版本和,历史最大值)的操作的讲解都不够直观。

核心思想就是在线段树中维护两个数组 \(A\) 和 \(B\) ,\(A\) 是普通线段树维护的东西,\(B\) 是历史版本的信息,然后有两种操作,一种操作是对于 \(A\) 的修改,另一种就是将 \(A\) 中的一个线段的信息通过一种方式敲到 \(B\) 上,将这一过程记为 \(A\rightarrow B\) 。

首先有两种 tag,一种是一个对于 \(A\) 的操作,设为 S,一种是对于 \(A\rightarrow B\) 的 tag ,记为 T

然后问题就出来了,因为最后 tag 会堆积成这个样子:

\[S\cdots S\ T\ S\cdots S\ T\ S\cdots S\ \]

然后就要处理这种东西,其实就是考虑旧的 \(S\) 对于新来的 \(T\) 造成的影响。

这里以区间加,历史版本和为例。

设 \(A_v\) 表示线段树节点所对应区间 \(A\) 数组的和,\(B_v\) 表示所对应 \(B\) 数组的和。

区间加大家都会,对于每个节点维护一个加法 tag \(S_i\) 记录将要对于所有子节点的 \(A\) 将要加上的值。

考虑对于线段树节点 \(v\) 儿子 \(s\) 的 \(B_s\) 的贡献。

首先单纯没有 \(S\) 的记录非常简单,就是 \(T\) 的次数乘上原来儿子上存的值,这里存一个 tag \(T_v\) 表示这个系数。

然后就是上方所说旧的 \(S\) 对于每个 \(T\) 的影响,对于每个 \(T\) 最后的贡献就是 \(len_s\times \sum S\) ,其中这个 \(len_v\) 表示区间长度,\(\sum S\) 表示在当前 \(T\) 操作时加法标记的总和,再开一个 tag \(t_v\) 表示所有 \(\sum S\) 的总和。

最后 pushdown 的操作就成了。

先下放 \(T_v\) 和 \(t_v\) ,再下放 \(S_v\) 就可以了。

上一篇:第五章:(3)Docker 之 常用命令&操作


下一篇:2021.12.16 输入一个字符串,内有数字和非数字字符,统计其*有多少个整数