标签:作用 子集 个数 算法 logs begin 算法实现 实现 过程
这一个内容在数论和组合计数类数学相关里面提到了。
若 \(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\) 的点值然后再插回去。
值得注意的是为了便于表述以及规范表达,快速莫比乌斯变换就相当于点值,快速莫比乌斯反演就相当于插值。
至此算法原理及过程已经完全结束。似乎我们可以用 \(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);
}
标签:作用 子集 个数 算法 logs begin 算法实现 实现 过程
原文地址:https://www.cnblogs.com/NaVi-Awson/p/9242645.html