前缀和 & 差分


前缀和

定义

前缀和可以简单理解为「数列的前 $n$ 项的和」,是一种重要的预处理方式,能大大降低查询的时间复杂度。

二维/多维前缀和

多维前缀和的普通求解方法几乎都是基于容斥原理。

基于 DP 计算高维前缀和

基于容斥原理来计算高维前缀和的方法,其优点在于形式较为简单,无需特别记忆,但当维数升高时,其复杂度较高。这里介绍一种基于 DP 计算高维前缀和的方法。该方法即通常语境中所称的 高维前缀和

设高维空间 $U$ 共有 $D$ 维,需要对 $f[\cdot]$ 求高维前缀和 $\text{sum}[\cdot]$。令 $\text{sum}[i][\text{state}]$ 表示同 $\text{state}$ 后 $D - i$ 维相同的所有点对于 $\text{state}$ 点高维前缀和的贡献。由定义可知 $\text{sum}[0][\text{state}] = f[\text{state}]$,以及 $\text{sum}[\text{state}] = \text{sum}[D][\text{state}]$。

其递推关系为 $\text{sum}[i][\text{state}] = \text{sum}[i - 1][\text{state}] + \text{sum}[i][\text{state}’]$,其中 $\text{state}’$ 为第 $i$ 维恰好比 $\text{state}$ 少 $1$ 的点。该方法的复杂度为 $O(D \times |U|)$,其中 $|U|$ 为高维空间 $U$ 的大小。

一种实现的伪代码如下:

for state
  sum[state] = f[state];
for(i = 0;i <= D;i += 1)
  for 以字典序从小到大枚举 state
    sum[state] += sum[state'];

树上前缀和

设 $\textit{sum}_i$ 表示结点 $i$ 到根节点的权值总和。
然后:

  • 若是点权,$x,y$ 路径上的和为 $\textit{sum}_x + \textit{sum}_y - \textit{sum}\textit{lca} - \textit{sum}{\textit{fa}_\textit{lca}}$。
  • 若是边权,$x,y$ 路径上的和为 $\textit{sum}_x + \textit{sum}y - 2\cdot\textit{sum}{lca}$。

差分

解释

差分是一种和前缀和相对的策略,可以当做是求和的逆运算。

这种策略的定义是令 $b_i=\begin{cases}a_i-a_{i-1},&i \in[2,n] \ a_1,&i=1\end{cases}$

性质

  • $a_i$ 的值是 $b_i$ 的前缀和,即$a_n=\sum\limits_{i=1}^nb_i$
  • 计算 $a_i$ 的前缀和 $sum=\sum\limits_{i=1}^na_i=\sum\limits_{i=1}^n\sum\limits_{j=1}^{i}b_j=\sum\limits_{i}^n(n-i+1)b_i$
    它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的数。注意修改操作一定要在查询操作之前。

树上差分

树上差分可以理解为对树上的某一段路径进行差分操作,这里的路径可以类比一维数组的区间进行理解。例如在对树上的一些路径进行频繁操作,并且询问某条边或者某个点在经过操作后的值的时候,就可以运用树上差分思想了。

树上差分通常会结合 树基础最近公共祖先 来进行考察。树上差分又分为 点差分边差分,在实现上会稍有不同。

点差分

举例:对树上的一些路径 $\delta(s_1,t_1), \delta(s_2,t_2), \delta(s_3,t_3)\dots$ 进行访问,问一条路径 $\delta(s,t)$ 上的点被访问的次数。

对于一次 $\delta(s,t)$ 的访问,需要找到 $s$ 与 $t$ 的公共祖先,然后对这条路径上的点进行访问(点的权值加一),若采用 DFS 算法对每个点进行访问,由于有太多的路径需要访问,时间上承受不了。这里进行差分操作:
$$
d_s \leftarrow d_s+1\
d_{lca} \leftarrow d_{lca} -1\
d_t \leftarrow d_t+1\
d_{d\left(lca\right)}\leftarrow d_{d\left(lca\right)}-1
$$
其中 $f(x)$ 表示 $x$ 的父亲节点,$d_i$ 为点权 $a_i$ 的差分数组。

可以认为公式中的前两条是对蓝色方框内的路径进行操作,后两条是对红色方框内的路径进行操作。不妨令 $\textit{lca}$ 左侧的直系子节点为 $\textit{left}$。那么有 $d_{\textit{lca}}-1=a_{\textit{lca}}-(a_{\textit{left}}+1)$,$d_{f(\textit{lca})}-1=a_{f(\textit{lca})}-(a_{\textit{lca}}+1)$。可以发现实际上点差分的操作和上文一维数组的差分操作是类似的。

边差分

若是对路径中的边进行访问,就需要采用边差分策略了,使用以下公式:
$$
d_s \leftarrow d_s + 1\
d_t \leftarrow d_t + 1\
d_{lca} \leftarrow d_{lca} - 2
$$

由于在边上直接进行差分比较困难,所以将本来应当累加到红色边上的值向下移动到附近的点里,那么操作起来也就方便了。对于公式,有了点差分的理解基础后也不难推导,同样是对两段区间进行差分。


文章作者: Jarrycow
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jarrycow !
评论
 上一篇
二分查找 二分查找
二分查找,也称折半搜索、对数搜索,是用来在一个有序数组中查找某一元素的算法。
2023-03-24
下一篇 
贪心 贪心
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。
2023-03-24
  目录