关于历史和线段树
这个线段树是用来解决形如以下的问题的(模板题 loj193):
- 区间加。
- 区间查询历史所有版本的和。
做法一
考虑维护 \(c_i = \texttt{hsum}_i - t \times a_i\),其中 \(t\) 为时间戳。
那么每次更新 l r x 时就将 \(c_i ← c_i - t \times x\),查询时的答案就是 \(\texttt{sum}_i \times t + c_i\),只需要维护一个普通的区间加线段树即可。类似于计算每个时刻的 \(a_i\) 对历史和贡献了多少,反正显然是正确的。代码。
(可能)适用性不高。
做法二
大多人看到这个问题的第一反应应该是维护值 \(\texttt{sum}\),\(\texttt{hsum}\) 和懒标记 \(\texttt{add}\)。对于每次加 \(x\),我们都需要将 \(\texttt{sum}_i ← \texttt{sum}_i + x \times (r - l + 1)\),\(\texttt{add}_i ← \texttt{add}_i + x\)。但是,可以发现这样直接维护无法正确的将 \(x\) 的贡献加到 \(\texttt{hsum}\) 上。
定义另一种懒标记(或者应该叫一次操作)upd 为对历史和贡献一次,即为 \(\texttt{hsum}_i ← \texttt{hsum}_i + \texttt{sum}_i\)。考虑一次 pushdown 是怎么样的。
对于一个位置 \(x\) 和他的某个儿子 \(s\),设这两个节点上积压了两坨没有下传的懒标记集合为 \(A\) 和 \(B\)。一次 pushdown,就是计算 \(A\) 对 \(B\) 产生的影响。
设 \(A\) 里面有 \(cnt\) 次 upd,则相当于 \(\texttt{sum}_s\) 对 \(\texttt{hsum}_s\) 产生了 \(\texttt{cnt}_x\) 次贡献,而剩下的加懒标记则一并对 \(\texttt{hsum}_s\) 产生了一次贡献(系数为 \(r - l + 1\)),即
\(\texttt{hsum}_s ← \texttt{hsum}_s + \texttt{cnt}_x \times \texttt{sum}_s + \texttt{hadd}_x \times (r - l + 1)\).
其中 \(\texttt{hadd}\) 表示历史懒标记和。
(为什么这样是对的?因为儿子节点的懒标记时间比父节点的懒标记总是靠前,所以可以这样做。)
结合上面的内容,我们发现我们只需要维护值 \(\texttt{sum}\),\(\texttt{hsum}\) 和懒标记 \(\texttt{add}\),\(\texttt{cnt}\) 和 \(\texttt{hadd}\) 即可。而上文所说的集合具体是什么顺序,我们不需要去关心,可以只用上述的东西刻画出来。
做法三
矩阵乘法。咕咕咕。
例题
首先离线扫描线,单调栈维护一下,将任务转化为:维护一个数据结构,使得他支持:
- 对数组 \(X\) 区间加。
- 对数组 \(Y\) 区间加。
- 求 \(\sum\limits_{i = l}^r X_i \times Y_i\) 的历史和。
(其中,我们利用单调栈独特的性质可以把区间赋值转化为若干个区间加以更好做,此时区间个数是 \(\mathcal{O}(n)\) 量级的。)
如果我们不需要维护历史和而是单纯的区间查询,则我们显然需要维护值 \(\texttt{sum}\),\(\texttt{sumx}\),\(\texttt{sumy}\) 和懒标记 \(\texttt{addx}\) 和 \(\texttt{addy}\)。剩下四个转移是简单的,\(\texttt{sum}\) 的转移可以简单推出是
\(\texttt{sum} ← \sum\limits_{i = l}^r (X_i + \texttt{addx})(Y_i + \texttt{addy}) = \sum\limits_{i = l}^r X_i \times Y_i + X_i \times \texttt{addy} + Y_i \times \texttt{addx} + \texttt{addx} \times \texttt{addy} = \texttt{sum} + \texttt{sumx} \times \texttt{addy} + \texttt{sumy} \times \texttt{addx} + \texttt{addx} \times \texttt{addy} \times (r - l + 1)\).
(注意区分下标)
考虑引入历史版本和。类似的,我们增加值 \(\texttt{hsum}\) 和懒标记 \(\texttt{cnt}\),\(\texttt{haddx}\),\(\texttt{haddy}\) 和 \(\texttt{haddxy}\)(其中 \(\texttt{haddxy} = \texttt{haddx} \times \texttt{haddy}\)),则转移易知为
及
\(\texttt{haddxy}_s ← \texttt{haddxy}_s + \texttt{addx}_s \times \texttt{addy}_s \times \texttt{cnt}_x + \texttt{addx}_s \times \texttt{haddy}_x + \texttt{addy}_s \times \texttt{haddx}_x + \texttt{haddxy}_x\).
复杂度 \(\mathcal{O}(n \log n)\)。
代码。

历史和线段树
浙公网安备 33010602011771号