集合幂级数

定义

所谓集合幂级数,就是以集合作为幂的级数。

\(U=\{1,2,3,\cdots,n\}\) 为全集,则集合幂级数为 \(f(x)=\sum_{S\subseteq U}f_Sx^S\)

如果把集合 \(S\) 中每一个数有没有压成一个二进制数,那么也可以看成是 \(n\)\(\bmod\space x_i^2(i=1,2,3,\cdots n)\) 的多元幂级数。

卷积

要定义两个集合幂级数的卷积,首先要定义 \(x^S\times x^T\) 的结果是什么。

根据实际应用,大致可以分为以下几类:

  • \(x^S\times x^T=x^{S\cup T}\):集合并卷积,也被称为 or 卷积。
  • \(x^S\times x^T=x^{S\cap T}\):集合交卷积,也被称为 and 卷积。
  • \(x^S\times x^T=x^{S\oplus T}\):集合异或卷积,也被称为 xor 卷积。

以上三种卷积我们都可以做到 \(O(n2^n)\) 计算。

还有一类较为特殊:

\[x^S\times x^T= \begin{cases} x^{S\cup T},S\cap T=\emptyset\\ 0,S\cap T\neq \emptyset \end{cases} \]

这种卷积被称为子集卷积,可以在 \(O(n^22^n)\) 的时间内计算。

然而使用上述集合幂级数的描述还是太抽象了,事实上可以把一个集合看成一个 \(n\) 位的二进制数,一个形式幂级数就是长为 \(2^n\) 的一个序列。集合并就是按位或,集合交就是按位与,集合异或就是按位异或。

FMT 快速莫比乌斯变换

FMT 用来快速计算集合并卷积和集合交卷积。

以集合并卷积为例,已知两个数列 \(a,b\),要求解 \(c\) 满足 \(c_S=\sum_{T\cup U=S}a_Tb_U\)

暴力枚举两个序列中的两项,复杂度 \(O(4^n)\),无法接受。

回顾多项式卷积的过程,是先用 FFT 变换出这个多项式的点值序列,然后用点值相乘,再逆变换回来。

仿照 FFT,设序列 \(a\) 的 FMT 变换 \(A\)\(A_S=\sum_{T\subseteq S}a_T\),则有:

\[\begin{aligned} C_S&=\sum_{T\subseteq S}c_T\\ &=\sum_{T\subseteq S}\sum_{U\cup V=T}a_Ub_V\\ &=\sum_{U\cup V\subseteq S}a_Ub_V\\ &=(\sum_{U\subseteq S}a_U)(\sum_{U\subseteq S}b_U)\\ &=A_SB_S \end{aligned}\]

所以只需要求出 \(a,b\) 的 FMT 变换然后点值相乘求出 \(C\) 再逆变换回去就好了。

现在要做 FMT 变换,直接枚举子集可以做到 \(O(3^n)\),但是还是不够快。

考虑位运算每一位的独立性,按位做贡献。从小到大考虑每一位,假设当前位为 \(x\),若 \(x\notin S\),则将 \(a_{S\cup\{x\}}\) 加上 \(a_S\)

其实就是高维前缀和。这样做复杂度是 \(O(n2^n)\) 的,效率很高。

逆变换直接把加换成减,也就是高维差分,同样是 \(O(n2^n)\) 的。

集合交卷积把高维前缀和换成高维后缀和就行。

void FMTor(int *f,int n,int mode){
	for(int w=1;w<n;w<<=1)
		for(int i=0;i<n;i+=(w<<1))
			for(int j=0;j<w;j++)
				f[i+w+j]=(f[i+w+j]+mode*f[i+j]+mod)%mod;
}
void FMTand(int *f,int n,int mode){
	for(int w=1;w<n;w<<=1)
		for(int i=0;i<n;i+=(w<<1))
			for(int j=0;j<w;j++)
				f[i+j]=(f[i+j]+mode*f[i+w+j]+mod)%mod;
}

FWT 快速沃尔什变换

FWT 用来快速计算集合异或卷积。

但是想要用人类智慧直接构造出 FWT 变换序列还是太困难了,考虑仿照 FMT 变换。

如果把一个序列 \(a\) 看做一个列向量,那么 FMT 变换可以看做一个矩阵 \(T\) 乘上 \(a\) 得到了 \(A\),对于 FWT 也就是:

\[\begin{aligned} (T\times a)\cdot(T\times b)&=T\times (a*b)\\ (T\times a)_i\times (T\times b)_i&=(T\times (a*b))_i\\ (\sum_jT_{i,j}a_j)(\sum_kT_{i,k}b_k)&=\sum_jT_{i,j}(a*b)_j\\ \sum_j\sum_ka_jb_kT_{i,j}T_{i,k}&=\sum_j\sum_{x\oplus y=j}a_xb_yT_{i,j}\\ \sum_j\sum_ka_jb_kT_{i,j}T_{i,k}&=\sum_x\sum_ya_xb_yT_{i,x\oplus y} \end{aligned}\]

于是可以得到这个矩阵 \(T\) 必须满足 \(T_{i,j}T_{i,k}=T_{i,j\oplus k}\),因为还要计算逆变换,其还要可逆。

手动构造这个矩阵还是太累了,考虑位运算对于每一位的独立性。假如能手动构造出 \(2\times 2\) 的矩阵 \(T_0\) 满足上述条件,那么只要令 \(T_{i,j}=\prod_{x=1}^nT_{0,[x\in i],[x\in j]}\) 即可,证明:

\[\begin{aligned} T_{i,j}T_{i,k}&=\prod_{x=1}^nT_{0,[x\in j],[x\in i]}\prod_{y=1}^nT_{0,[y\in k],[y\in i]}\\ &=\prod_{x=1}^nT_{0,[x\in i],[x\in j]}T_{0,[x\in i],[x\in k]}\\ &=\prod_{x=1}^nT_{0,[x\in i],[x\in j]\oplus[x\in k]}\\ &=T_{i,j\oplus k} \end{aligned}\]

经过手玩可以发现,\(T_0=\begin{bmatrix}1&1\\1&-1\end{bmatrix}\)\(T^{-1}=\begin{bmatrix}\frac{1}{2}&\frac{1}{2}\\\frac{1}{2}&-\frac{1}{2}\end{bmatrix}\)

依然考虑位运算对于每一位的独立性,从小到大考虑每一位,假设当前位为 \(x\),若 \(x\notin S\),设 \(T=S\cup\{x\}\),则上面那个矩阵可以直接套过来他们两个的贡献:\(\begin{bmatrix}a_S\to a_S=1&a_S\to a_T=1\\a_T\to a_S=1&a_T\to a_T=-1\end{bmatrix}\)

void FWT(int *f,int n,int mode){
	for(int w=1;w<n;w<<=1)
		for(int i=0;i<n;i+=(w<<1))
			for(int j=0;j<w;j++){
				int x=f[i+j],y=f[i+w+j];
				f[i+j]=(x+y)%mod,f[i+w+j]=(x-y+mod)%mod;
				if(mode==-1)f[i+j]=f[i+j]*inv2%mod,f[i+w+j]=f[i+w+j]*inv2%mod;
			}
}

线性性

通过上面对 FWT 的矩阵乘法形式理解,可以发现 FWT 是线性变换,套用这个东西可以得到 FMT 也是线性变换。

也就是说 \(\operatorname{FWT}(a)+\operatorname{FWT}(b)=\operatorname{FWT}(a+b)\)\(c\operatorname{FWT}(a)=\operatorname{FWT}(ca)\)

例题

CF449D

容斥一下。and 为 \(0\) 这个条件相当于每一位至少有一个为 \(0\),设 \(f_S\) 表示 and 必须为 \(S\) 的方案数,\(g_S\) 表示 and 是 \(S\) 的超集的方案数,答案为 \(f_0\)。可以得到关系式 \(g_S=\sum_{S\subseteq T}f_T\)\(f_0\) 可以通过子集反演由 \(g\) 求出。

考虑 \(g_S\) 怎么求。发现可以先用高维后缀和求出 \(S\) 超集有多少个数,然后在这些数里随便选就能构成超集,也就是 \(2^x\) 的贡献。


另外一种做法是集合幂级数。转化题目为求出:

\[[x^0]\prod_{i=1}^n(x^{a_i}+x^U) \]

其中 \(x^S\times x^T=x^{S\cap T}\)\(U\) 为全集。

注意到其中每一个多项式只有两项,不妨手玩一下观察规律,发现 $\operatorname{FMT}(x{a_i}+xU) 中每一位只可能是 \(1\) 或者 \(2\)

考虑到 FMT 的线性性,不妨先算出 \(f=\sum_{i=1}^n\operatorname{FMT}(x^{a_i}+x^U)=\operatorname{FMT}(\sum_{i=1}^n(x^{a_i}+x^U))\)。然后依次考虑每一位,它一定是由若干个 \(1\) 和若干个 \(2\) 加起来的,不妨设 \(x\)\(1\)\(y\)\(2\),于是可以得到方程组:

\[\begin{cases} x+y=n\\ x+2y=f_i \end{cases}\]

可以解出:

\[\begin{cases} x=2n-f_i\\ y=f_i-n \end{cases}\]

这样就可以求出每一位上有多少个多项式为 \(1\)\(2\),然后直接点乘起来再逆 FMT 回去即可。

CF662C

显然应该枚举翻转哪些行,假设翻转的行的集合为 \(S\),对每一列考虑,设第 \(i\) 列的数原来是 \(a_i\),如果翻转这一列能够让 \(1\) 变少,那么就翻转。设翻转行集合为 \(S\) 时的答案为 \(ans_S\),也就是说:

\[ans_S=\sum_{i=1}^m\min\{\operatorname{popcount}(a_i\oplus S),n-\operatorname{popcount}(a_i\oplus S)\} \]

答案即为 \(\min\{ans_S\}\)

这个形式看不出来啥东西,换一种表示。设 \(f_S\) 表示列值为 \(S\) 的有多少个,则有:

\[ans_S=\sum_Tf_T\min\{\operatorname{popcount}(T\oplus S),n-\operatorname{popcount}(T\oplus S)\} \]

\(g_S=\min\{\operatorname{popcount}(S),n-\operatorname{popcount}(S)\}\),则:

\[ans_S=\sum_Tf_Tg_{S\oplus T} \]

什么这不是我们 FWT 吗。

UOJ310

首先注意到如果能取出一个集合 \(S\) 其异或和为 \(0\),这个集合所有 \(2^{|S|}\) 种划分方案都符合条件。所以题目转化为一个异或和为 \(0\) 的集合 \(S\) 的贡献为 \(2^{|S|}\),求出所有的贡献。使用集合幂级数表示为:

\[[x^0]\prod_{i=1}^n(1+2x^{a_i}) \]

注意到每个多项式也是只有两项,手玩可以发现里面只可能有 \(-1\) 或者 \(3\)。所以仿照之前的做法解方程就做完了。

子集卷积

子集卷积相比普通的集合并卷积,增加了交集为空的限制。考虑把限制拆成 \(i\cup j=k\)\(|i|+|j|=|k|\)

对于每个 \(i=0,1,2,\cdots,n\),构造占位多项式 \(f_i(S)=\begin{cases}a_S,|S|=i\\0,|S|\neq i\end{cases}\)。同样对 \(b\) 序列构造 \(g_i(S)\)

对所有的 \(f_i(S)\)\(g_i(S)\) 做一遍 FMT 变换变为 \(F_i(S)\)\(G_i(S)\),然后枚举 \(i,j\),令 \(H_{i+j}(S)\)\(F_i(S)\)\(G_j(S)\) 的点值相乘,再将所有 \(H_i(S)\) 逆变换为 \(h_i(S)\),最终的答案序列 \(c\) 即为 \(c_S=h_{|S|}(S)\)。容易发现这样的值能被正确贡献。

时间复杂度 \(O(n^22^n)\)

如果要做形如 \(dp_S=\sum_{T\subset S}dp_Tg_{S-T}\) 的 DP,可以使用半半在线子集卷积。

同样地对 \(dp_S\) 构造 \(n+1\) 个占位多项式 \(f_i(S)\),按 \(i\) 从小到大的顺序计算即可。复杂度 \(O(2^nn^2)\)

例题

P3349

\(f_{u,i,S}\)\(u\) 子树内的点映射到 \(S\) 中,且 \(u\) 映射为 \(i\) 的方案数。转移相当于子集卷积,只保留 \(|S|=size\) 的位即可。\(O(2^nn^3)\)

P4221

\(v_S\) 表示 \(S\) 是否合法,可以 \(O(n2^n)\) 简单计算,\(w_S\)\(S\) 内点权和,\(f_S\)\(S\) 的答案,则有 DP:

\[f_S=\frac{1}{w_S ^p}\sum_{T\subset S}f_Sc_{S-T}w_{S-T}^p \]

使用半半在线子集卷积即可。

posted @ 2025-04-09 16:06  Linge_Zzzz  阅读(189)  评论(0)    收藏  举报