高维前缀和 & FMT
高维前缀和 & FMT
例题 ( ARC136D
):
给 \(n\) 个小于 \(10^6\) 的数 \(a[1\cdots n]\),求有多少对数相加后不仅进位。
做法:
考虑两数相加不进位,等价于两数拆分成十进制后每个相同位上的数之和不超过 \(9\)。
那么我们考虑枚举一个数,计算有多少个数,对十进制每一位,都满足小于某数码的限制。
这个问题就可以用高维前缀和解决。
具体来说,我们设 \(F(S)=\sum\limits_{S'}a_{S'}\),其中 \(S'\) 满足每一位都严格小于等于 \(S\) 的对应位,
在二进制下就是 \(F(S)=\sum\limits_{S'\subseteq S}a_{S'}\),高进制的也可以用高维数组的形式更清晰的写出来。
那么我们考虑如何快速计算 \(F(S)\) 的值,这时我们可以采用递推的方法,即:
记 \(f(i,S)=\sum\limits_{S'}a_{S'}\),满足 \(S'[0,i-1]\subseteq S[0,i-1],S'[i,n-1]=S[i,n-1]\)。
注意,因为高进制的方法不难从二进制下拓展,故为了方便书写,我们只讨论二进制下的情况。
我们考虑推出 \(f(i+1,S)\),发现其与 \(f(i,S)\) 唯一的变化是 \(S'\) 第 \(i\) 位可以取的值域,
那么我们就有:
初始时:\(f(-1,S)=a_S\);
递推时:
- \(f(i+1,S)=f(i,S)\),\(S[i]=0\);
- \(f(i+1,S)=f(i,S)+f(i,S\verb|\| \verb|{i}|)\),\(S[i]=1\).
而最后的 \(F(S)\) 就等于 \(f(n,S)\)。
这种递推的方法,显然在任意进制下都有很好的应用空间,
而应用在高进制下的这种方法,实际上就是一种维护高维前缀和的有力手段。
实际上,上述算法的过程还是是可逆的,就是说我们可以通过一些手段从 \(F\) 快速倒推回 \(a\)。
这里我们考虑高维前缀和的另一个递推式,即利用了容斥思想写出的递推式:
\(F(S)=a_{S}+\sum\limits_{S'\subset S}(-1)^{|S|-|S'|+1}F(S')\),
即考虑与 \(S\) 全相等的,有一位不同的,有两位不同的,\(\cdots\) 并将这些类的贡献分别乘上系数后求和。
我们考虑将式子做一些转化,并用 \(F\) 去表示 \(a\),即:
\(a_{S}=F(S)-\sum\limits_{S'\subset S}(-1)^{|S|-|S'|+1}F(S')\)
\(= F(S)+\sum\limits_{S'\subset S}(-1)^{|S|-|S'|}F(S')\)
\(= \sum\limits_{S'\subseteq S}(-1)^{|S|-|S'|}F(S')\).
注意到,此时的逆表达式竟然和原来的正表达式非常相像,唯一相差的就是一个 \(-1\) 幂次的系数。
那么,我们依葫芦画瓢,和正表达式的快速计算类似,我们仍然用递推的方法解决问题。
那么,这时我们设 \(A(i,S)=\sum\limits_{S'}F_{S'}\),满足 \(S'[0,i-1]\in S[0,i-1],S'[i,n-1]=S[i,n-1]\)。
类似的,我们考虑表示 \(A(i+1,S)\) 与 \(A(i,S)\) 间的变化量来递推,只需要注意乘上系数,即:
初始时:\(A(-1,S)=F(S)\);
递推时:
- \(A(i+1,S)=A(i,S)\),\(S[i]=0\);
- \(A(i+1,S)=A(i,S)-A(i,S\verb|\| i)\),\(S[i]=1\).
那么这就是逆推的方法。
这个逆推和正推结合在一起,就是我们所说的快速莫比乌斯变换,即 FMT
,
它的一个应用场景是计算集合并卷积以及集合交卷积,即以下题目:
我们有两个序列 \(A,B\),需要求序列 \(C_i=\sum\limits_{j|k=i}A_jB_k\)。
我们考虑用 FMT
来快速解决上述问题,大体思路和 FFT
类似,
只不过把点值与系数间的变换,变成了原序列与子集和序列的变换。
具体来说,对于任意序列 \(X\),我们记 \(X'_i=\sum\limits_{j\in i}X_j\),则我们有:
\(C'_i=\sum\limits_{j|k\subseteq i}A_jB_k=\sum\limits_{j\subseteq i, k\subseteq i}A_jB_k=(\sum\limits_{j\subseteq i}A_j)(\sum\limits_{k\subseteq i}B_k)=A'_iB'_i\),
这个式子意味着,我们只要快速计算出 \(A'\) 和 \(B'\),就能快速计算出 \(C'\),再逆推回去就得到了 \(C\)。
以上就是集合并卷积的做法,而集合交卷积可以通过容斥的思想来完全转化成集合并卷积。
文章至此也就结束了,至于时间复杂度,显然是 \(O(n2^n)\) 的,其中 \(n\) 是需要变换的序列的长度取对数。