差分与前缀和
前情提要:学弟说想学莫反,于是偶先写一个入门教程,后面再写不入门的。欢迎大家提出建议,请大力喷!
差分、前缀和作为微分、积分在离散空间中的对应形式,还是相当有用的。
Part 1
1D 序列 / 一维线性空间
这是我们最熟悉的场景,说给定了一个序列 \(a=(a_1,a_2 \dots a_n)\),可以定义差分、前缀和两种运算/算子。
定义差分算子 \(\Delta\) 作用效果是 \((\Delta a)_i = a_i - a_{i-1}\)。
定义前缀和算子 \(\Delta^{-1}\) 作用效果是 \((\Delta^{-1} a)_i = \sum_{x=1}^i a_x\)。
这个符号特别难打,后面基本就不用了,只是想说这两种操作是互逆的。
这里注意一个细节,学过微积分的同学会知道,我们先差分再前缀和,应该会多出来一个不能确定的 \(C\)。
举个例子就是 \((1,2,3), (10,11,12)\) 的差分都是 \((1,1)\),再做前缀和得到的是 \((C, C+1, C+2)\) 这样一簇东西。
这样一来,我们的差分算子不是单射,疑似有点太垃圾了。
所以我们总是额外规定 \(a_0 = 0\),这样在差分的时候,可以额外保存这个常数 \(C\) 的具体信息。
这样一来 \(\Delta\) 和 \(\Delta^{-1}\) 之间也就可以交换次序了,即 \(\Delta \Delta^{-1} = \Delta^{-1} \Delta = I\)。当然这个很平凡。
Q1:给定长为 \(n\) 的序列 \(a_i\),有 \(q\) 次操作,支持区间加一个数,全部结束后,你需要输出序列。
Sol:维护差分序列,最后前缀和还原回来就行,\(O(n+q)\)。
Q2:给定长为 \(n\) 的序列 \(a_i\),有 \(q\) 次操作,支持区间加等差数列,全部结束后,你需要输出序列。
Sol:维护二阶差分序列,最后二阶前缀和还原回来就行,\(O(n+q)\)。
反演
反演的意思是,通过一个已知求和的序列,反向把原序列解出来。所以差分其实就是一种反演。
假设原序列是 \(a\),前缀和数组是 \(A\),那么可以写出以下这种等式:
- \(A_1 = a_1\),\(A_2 = a_1 + a_2\),\(A_3 = a_1 + a_2 + a_3\)
考虑把前缀和算子 \(\Delta^{-1}\) 写成矩阵乘法:
考虑把差分算子 \(\Delta\) 写成矩阵乘法:
观察这两个矩阵,会发现一个“巧合”,两者是互逆的。
也就是说反演,或者说求差分算子 \(\Delta\),其实就是求某个矩阵的逆矩阵。也可以认为反演就是容斥。我们后面还会反复用到这种刻画。
加法卷积
我们定义两个序列 \(a\) 和 \(b\) 的加法卷积为:
故可以使用卷积来描述前缀和、差分的算子:
- 定义 \(I = (1,1,1,1 \dots 1)\) 表示常数函数,那么前缀和 \(A\) 就是 \(a * I\) 做卷积。
- 定义 \(D = (1,-1,0,0 \dots 0)\) 表示差分函数,那么差分 \(a\) 就是 \(A * D\) 做卷积。
- 定义 \(e = (1, 0,0,0 \dots 0)\) 为单位元函数。类似的,也有 \(I * D = e\) 互逆。
这里的常数函数有时也记作 \(\textbf{1}\) 或 \(\textbf{1}(n)\) 的形式。这种做法也可以视作是形式幂级数、生成函数。
高维前缀和 / 高维线性空间
先考虑 2D 的版本,这个也比较众所周知了:
前缀和,意思是,对于两个维度上,都满足下标都小于等于我的位置,它们的值要被加给我:
这里定义偏序关系是 \(\le\) 即整数的大小关系。
差分,即容斥,这里的容斥系数容易确定:若下标有 \(c\) 个维度上有减法,系数就是 \((-1)^c\):
可以把上式的减一换成别的,这样会容斥出来一个矩形范围。我们后面还会继续提到这点。
求前缀和的时候可以逐维度叠加,这里直接偷 OI wiki 的代码,以三维为例:
for (int i = 1; i <= N1; ++i)
for (int j = 1; j <= N2; ++j)
for (int k = 1; k <= N3; ++k) a[i][j][k] += a[i][j][k - 1];
for (int i = 1; i <= N1; ++i)
for (int j = 1; j <= N2; ++j)
for (int k = 1; k <= N3; ++k) a[i][j][k] += a[i][j - 1][k];
for (int i = 1; i <= N1; ++i)
for (int j = 1; j <= N2; ++j)
for (int k = 1; k <= N3; ++k) a[i][j][k] += a[i - 1][j][k];
设 \(k\) 维空间的整体大小为 \(n\),则前缀和操作的复杂度为 \(O(kn)\),差分也一样。
(原地)差分的代码需要改 -=,并使枚举顺序反向(从大到小),因为我们希望等式右侧的变量还没有被还原回去,也可以理解为按顺序撤销。
SOSdp / 快速 Zeta 变换
考虑这样一个问题,我们有一个大小为 \(n\) 的集合 \(S\),有一个在全体子集上有定义的 \(f\),希望对每个子集 \(S'\) 都计算:
枚举每个子集 \(S'\) 再枚举其子集,这样的复杂度是 \(O(3^n)\) 的,但我们可以做得更好。
不妨将每个元素视作一个维度,将不含/含它分别设为 \(\{0,1\}\),则得到了 \(n\) 维空间,总状态数为 \(2^n\)。
我们是这样描述前缀和的:对于每个维度上,都满足下标都小于等于我的位置,它们的值要被加给我。
我们定义偏序关系 \(\subseteq\) 即集合的包含关系,子集和就变成了前缀和的描述,可以得到 \(O(n 2^n)\) 的算法。
g = f; // 先把 f(T) 加给 g(T),类比一下 a 和 A
for (int i = 0; i < n; ++i) { // 考虑每个维度(逐维度叠加)
for (int st = 0; st < (1 << n); ++st) { // 考虑每个状态(从小到大)
if ((st >> i) & 1) { // 该状态第 i 个维度是 1
// g[... 1 ...] += g[... 0 ...].
g[st] += g[st ^ (1 << i)]; // 右侧的状态一定已经算过了
}
}
}
一般来说,如果希望差分回去的话,首先需要改成 -=,同时翻转状态的枚举顺序(从大到小)。
但由于这里每个维度只有 \(\{0,1\}\),我们并不需要翻转枚举顺序,因为右侧的变量在枚举每个 \(i\) 时不可能被修改。
Part 2
算术基本定理 / 素数基
也就是整数唯一分解定理,任何正整数 \(n\) 都可以唯一表示为素数的乘积:
我们不妨把所有素数看做一组基,那么每个正整数 \(n\) 都对应了一个向量:
考虑一个数 \(n\) 的所有约数 \(d\),实际上遍历了每一个维度上都 \(\le\) 向量 \(\vec{n}\) 的 \(\vec{d}\)。
积性函数 / 正交分解
数论函数(也称算术函数)指定义域为正整数的函数。数论函数也可以视作一个数列。
积性函数是一类特殊的数论函数,满足:\(f(1) = 1\),如果 \(\gcd(a, b) = 1\),则 \(f(ab) = f(a)f(b)\)。
这类函数我们经常使用,从素数基的视角来看,\(\gcd(a, b) = 1\) 意味着 \(\vec{a}\) 和 \(\vec{b}\) 是正交的。
这样一来,我们的 \(f(n)\) 总是可以这样表示:
换句话说,对于具有积性的函数,我们只要研究它在单个素数轴上的性质,就足够考察它在所有整数上的性质了。
注:众所周知 \(\gcd(a,b) = 1\) 还可以写作 \(a \perp b\),这其实正是想要表示两者正交的意思。
狄利克雷卷积 / 乘法卷积
给定两个数论函数 \(f\) 和 \(g\),它们的狄利克雷卷积定义为:$$(f * g)(n) = \sum_{d\vert{}n} f(d) g\left(\frac{n}{d}\right)$$
我们考虑把数字表示成素数基的向量,那么 \(\frac{n}{d}\) 的坐标恰好是向量相减 \(\vec{n} - \vec{d}\)。上面的公式就变成了:
对照一下,可以发现其实就是加法卷积公式,在高维空间的推广。
若只求一项 \((f * g)(n)\) 的值,分解因子就能做到 \(O(\sqrt n)\);否则可以暴力枚举,在 \(O(n \log n)\) 时间求解每项:
for (int k = 1; k <= n; k++)
for (int d = 1; k * d <= n; d++)
h[k * d] += f[k] * g[d];
狄利克雷前缀和
令 \(g(n) = 1(n)\),然后和 \(f\) 做卷积,可以发现其实 \(F(\vec{n})\) 就是 \(f\) 在这个空间里的前缀和:
我们可以逐维度完成前缀和,逻辑和埃筛一致,故复杂度也为 \(O( n \log\log n)\),代码如下:
F = f; // 先把 f(i) 加给 F(i)
for (int x = 2; x <= n; x++) {
if (vis[x]) continue; // 枚举每个质数维度
for (int y = 1, xy = x; xy <= n; y++, xy += x) {
vis[xy] = true;
F[xy] += F[y];
}
} // 差分时记得反向枚举内层的 xy
初学的时候可能会写出 F[xy] += F[xy - x] 这种错误,只要提醒自己,我们的整数乘除才是向量上的加减法。
不妨将狄利克雷前缀和 \(A_n = \sum_{d\vert{}n} a_d\) 用矩阵乘法表示:
我们如果希望通过 \(A\) 反求 \(a\),就需要求上面的逆矩阵,而在“素数基”张成的空间里,逆矩阵的系数亦可以用莫比乌斯函数 \(\mu\) 确定。
莫比乌斯反演 / 容斥
回忆 2D 矩阵的容斥:下标要么不变要么减一;若下标有 \(c\) 个维度上有减法,系数就是 \((-1)^c\):
在素数基下,我们也可以定义一样的规则,和普通容斥无异:
- 若无减法,则系数为 \(1\)
- 若有 \(c\) 个素数维度减一,则系数为 \((-1)^c\)
- 如果有位置出现了 \(-2\) 等其他值,系数为 \(0\)
我们令被减去的素数之乘积 \(\prod p_i^{c_i} = n\),则可以这样定义莫比乌斯函数(容斥系数):
这样一来就可以更简洁地表达 \(f\) 与其狄利克雷前缀和 \(F\) 的关系:
这就是莫反的约数形式。只需要记住,这里的除法可以写成向量减法,上式就会非常容易理解,并且可以当场推导。
转化为矩阵语言,前一节我们想要求得的逆矩阵:第 \(i\) 行、第 \(d\) 列的元素在 \(d \mid i\) 时为 \(\mu(i/d)\),若不能整除则为 \(0\)。
接下来我们介绍莫反的倍数形式,这是与莫反的约数形式一样热门的形式:(TOP2 常用!)
即狄利克雷后缀和,以及其反演形式。求和的上界是到 \(kn\) 超过定义域为止。
怎么理解 \(kn\) 呢,前缀和在偏序上小于我的是做除法,那么后缀和就是乘法。
常见公式
综上,我们总结一下可以和这篇入门教程产生关联的公式,并简单推导,说明正确性。
约数个数
定义 \(d(n)\) 表示 \(n\) 的约数个数,是积性函数。写成前缀和就是 \(d(n) = \sum_{d\vert{}n} 1\)。
用素数基的看法就是从原点 \(\vec{0}\) 到 \(\vec{n} = (c_1, c_2, c_3, \dots)\) 构成的超长方体里,一共有多少个整数格点,故:
考虑补成乘法卷积形式 \(d(n) = \sum_{d\vert{}n} 1 \cdot 1\),我们也就得到了 \(d = \textbf{1} * \textbf{1}\),如何理解呢:
把 \(d(n)\) 当做是对一个全 \(1\) 的空间做前缀和以后在 \(\vec{n}\) 处的值,可以拿 1D 数组类比一下。
莫比乌斯函数
莫比乌斯函数 \(\mu(n)\) 是积性函数。莫比乌斯函数有一个非常重要的恒等性质:
怎么理解呢,我们不妨把等式右边写成 $ \sum_{ d \mid n } \mu(d) \cdot 1$,考察其具体含义:
就是对一个全 \(1\) 的空间做差分,结果只有原点 \(n = 1 = (0,0,0 \dots)\) 的值是 \(1\),可以拿 1D 数组类比一下。
也就是证明了 \(\mu * \textbf{1} = e\),卷 \(1\) 是前缀和,卷 \(\mu\) 是差分,这两个操作互逆,结果是单位元 \(e\)。
欧拉反演
欧拉函数 \(\varphi(n) = \sum_{i=1}^n [i \perp n]\) 是一个积性函数,表示 \(1 \dots n\) 的数字中和 \(n\) 互质的数量。
有一个常用的欧拉反演公式 \(\sum_{d\vert{}n} \varphi(d) = n\),即 \(\varphi\) 在高维空间中的前缀和,我们试着证明它。
由于具有积性,我们只需要单独考察一个维度 \(p\) 上坐标为 \(k\) 时的情形,即 \(n = p^k\) 时:
把它们加起来,可以发现这里相邻项都相消了,恰好得到了欧拉反演的表述:
考虑补成乘法卷积形式 \(\sum_{d\vert{}n} \varphi(d) \cdot 1= n\),我们也就得到了 \(\varphi * \textbf{1} = \text{id}\)。
\(\varphi = \mu * \text{id}\)
试证明下面的恒等式: $$\varphi(n) = \sum_{d\vert{}n} \mu(d) \frac{n}{d}$$
我们上一段说明了 \(\varphi * \textbf{1} = \text{id}\),在两边同时卷上 \(\mu\),有:
我们知道卷 \(\textbf{1}\) 是前缀和,而卷 \(\mu\) 是差分,故 \(\textbf{1}\) 和 \(\mu\) 互逆:
得证。
感谢阅读,请喷。

浙公网安备 33010602011771号