写在前面
- 这是继数论和组合计数类数学相关与多项式类数学相关后的第三篇数学方面内容总结。主要记录自己近期学习的一些数学方法。内容比较杂,同时也起到对前文的一些补充作用。
- 若文章中出现错误,烦请告知。
- 感谢您的造访。
一类反演问题
莫比乌斯反演
这一个内容在数论和组合计数类数学相关里面提到了。
快速莫比乌斯变换(反演)与子集卷积
莫比乌斯变换(反演)
问题提出
若 \(h,f,g\) 为下标为集合的函数,我们定义
\[h=f*g\]
表示
\[h(S) = \sum_{L \subseteq S}^{} \sum_{R \subseteq S}^{} [L \cup R = S] f(L) \times g(R)\]
容易发现,对于这个问题,我们可以用 \(O\left((2^n)^2\right)\) 的枚举 \(L,R\) 来计算。
然而这样复杂度较高,我们考虑类比多项式卷积的过程,可以求出 \(f,g\) 的点值,直接相乘得到 \(h\) 的点值然后再插回去。
值得注意的是为了便于表述以及规范表达,快速莫比乌斯变换就相当于点值,快速莫比乌斯反演就相当于插值。
算法原理
- 我们定义 \(f\) 的莫比乌斯变换为 \(F\) ,其中 \(F(S)=\sum_{X\subseteq S}f(X)\) ;由这个定义,我们可以推出 \(F\) 莫比乌斯反演 \(f\) 为 \(f(S) = \sum_{X \subseteq S} (-1)^{|S| - |X|}F(X)\) 。对于莫比乌斯反演的证明,可以带入莫比乌斯变换的式子或容斥来证。
- 我们对于一个函数 \(f,g,h\) ,记它的点值式为 \(F,G,H\) 。我们将问题提出中的卷积式两边同时做莫比乌斯变换,得到 \[\begin{aligned}H(S) &= \sum_{L \subseteq S} \sum_{R \subseteq S} [L \cup R \subseteq S] f(L) \times g(R)\\&=\sum_{L \subseteq S} \sum_{R \subseteq S}f(L) \times g(R)\\&=\left(\sum_{L \subseteq S}f(L)\right)\times\left(\sum_{R \subseteq S}g(R)\right)\\&=F(S)\times G(S)\end{aligned}\]
至此算法原理及过程已经完全结束。似乎我们可以用 \(O(3^n)\) 枚举子集来变换和反演,实际上我们可以让复杂度更优。
算法实现
用高维前缀和可以做到 \(O(n\times 2^n)\) 的递推,求出点值和插值。
void FMT(int *A, int o) {// o 为识别因子
for (int i = 1; i < ST; i <<= 1)//ST-1 表示全集
for (int j = 0; j < ST; j++)
if (i&j) (A[j] += A[j^i]*o) %= mod;
}
子集卷积
\(\text{FWT}\) :“你刚才说的那个玩意我也能做啊,要你何用?”
\(\text{FMT}\) :“……”
问题提出
若 \(h,f,g\) 为下标为集合的函数,我们定义
\[h=f*g\]
表示
\[h(S) = \sum_{X \subseteq S} f(X) \times g(S-X)\]
算法实现
回顾刚刚的集合并卷积,子集卷积的条件比集合并卷积更苛刻,即 \(L\) 和 \(R\) 的集合应该不相交。
我们可以在卷积时多加一维,维护集合的大小,如 \(f_{i,S}\) 表示集合中有 \(i\) 个元素,集合表示为 \(S\) 。显然,当 \(i\) 和 \(S\) 的真实元素个数符合时才是对的。记数组 cnt[S]
表示集合 \(S\) 的模。初始时,我们只把 \(f_{cnt[S],S}\) 的值赋成原来的 \(f(S)\) ( \(g\) 同理),然后每一维做一遍 \(\text{FMT}\) ,点值相乘时这么写:\(h_{i, S} = \sum_{j = 0}^{i} f_{j,S} \times g_{i - j, S}\) 。最后扫一遍把不符合实际情况的状态赋成 \(0\) 即可。
for (int i = 0; i <= n; i++) FMT(g[i], 1);
for (int i = 0; i <= n; i++) FMT(f[i], 1);
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= i; j++)
for (int k = 0; k < ST; k++)
(h[i][k] += 1ll*f[j][k]*g[i-j][k]%yzh) %= yzh;
FMT(h[i], -1);
for (int k = 0; k < bin[n]; k++) if (cnt[k] != i) h[i][k] = 0;
if (i != n) FMT(h[i], 1);
}