集合幂级数

本文参考 2015 吕凯风(vfleaking)集训队论文《集合幂级数的性质与应用及其快速算法》

1. 集合幂级数

1.1 定义

\(F\) 是一个域, 称函数 \(f: 2^U \to F\)\(F\) 上的一个集合幂级数。对于每个 \(S \in 2^U\),记 \(f_S\) 表示把 \(S\) 带入函数 \(f\) 后的函数值, 并称 \(f_S\) 为该集合幂级数第 \(S\) 项的系数。

换句话说, 集合幂级数就是从全集的所有子集到某个域 \(F\) 上的映射,把 \(U\) 的每个子集 \(S\) 带入函数,均有对应的取值 \(f_S\)

1.2 表示

类比形式幂级数, 把每个集合 \(S\) 对应的取值 \(f_S\) 后添加占位符 \(x^S\) 即可。

例如 : \(f = x^{\emptyset} + x^{\{1\}}+4^{\{2\}}+5^{\{1, 2\}}+x^{\{3\}}+4^{\{1,2,3\}}\)

1.3 加法与乘法

加法 : 设 \(f, g, h\) 为一个集合幂级数, 定义 \(f+g=h\), 满足 : \(h_S = f_S +g_S\)

乘法 : 设 \(f, g, h\) 为一个集合幂级数。 \(*\) 为一个定义在集合上的二元运算(应该具有结合律,交换律, 对 \(+\) 有分配律)。

定义 \(f * g = h\), 满足 : \(h = (\sum\limits_{L \in 2^U}f_Lx^L)*(\sum\limits_{R\in2^U}g_Rx^R)\)

\(*\) 换成不同的运算可以得到不同的效果, 下面主要介绍集合并卷积,集合对称差卷积, 子集卷积。

2. 集合并卷积

我们取 \(L * R = L \cup R\) 就可得到集合并卷积。

\(f, g, h\) 为集合幂级数, 则 \(h_S = \sum\limits_{L\in2^U}\sum\limits_{R\in 2^U} [L \cup R = S]f_Lg_R\)

定义 \(f'_S = \sum\limits_{T \subseteq S} f_T\) 的变换为莫比乌斯变换, 又记作 \(\operatorname{FMT(F)}\)

定义 \(f_S = \sum\limits_{T \subseteq S}(-1)^{|S|-|T|}f'_T\) 的变换为莫比乌斯反演, 又记作 \(\operatorname{FMI}(F)\)

可以得到的是 \(\operatorname{FMT(F)}\)\(\operatorname{FMI(F)}\) 互为逆变换, 这是因为 :

\(f_S = \sum\limits_{T \subseteq S}(-1)^{|S| -|T|}\sum\limits_{U \subseteq T}f_U = \sum\limits_{U \subseteq S}f_U \sum\limits_{i=0}^{|S|-|U|}\dbinom{|S| - |U|}{i}(-1)^{|S|-|U|-i}\)

\((1-1)^{|S|-|U|} = [|S| = |U|] = [S = U]\), 当且仅当 \(U = S\) 时有 \(f_U\) 的贡献, 证毕。

对这个卷积左右两边都反演一下 :

\(h'_S =\sum\limits_L\sum\limits_R [L \cup R \subseteq S]f_Lg_R\)

\(h'_S = \sum\limits_L[L \subseteq S]f_L \sum\limits_R [R \subseteq S]g_R\)

\(h'_S = f'_Sg'_S\)

部分代码 :

inline void FWT_or(int *f, int opt) {
    for(int i = 1; i < n; i <<= 1) 
	    for(int j = 0; j < n; j += (i << 1)) 
	        for(int k = 0; k < i; ++k) f[i + j + k] = (f[i + j + k] + opt * f[j + k]) % mod; 
}

集合交卷积 :与集合并卷积类似, 只是反演柿子变成了 \(f'_S = \sum\limits_{S \subseteq T}(-1)^{|T| -|S|}f_T\)

inline void FWT_and(int *f, int opt) {
	for(int i = 1; i < n; i <<= 1) 
	    for(int j = 0; j < n; j += (i << 1)) 
	        for(int k = 0; k < i; ++k) f[j + k] = (f[j + k] + opt * f[i + j + k]) % mod;  

3. 集合对称差卷积

定义 :二元集合对称差表示 \(A \oplus B = \{x| (x \in A) \oplus(x\in B)\}\), 相当于异或运算符。

\(f, g, h\) 为集合幂集数, 定义 \(h_S= \sum\limits_L \sum\limits_R [L \oplus R = S] f_Lg_R\)

引入一个公式 : \([S = \emptyset] = \dfrac{1}{2^n}\sum\limits_{T} (-1)^{|S \cap T|}\)

\(S\) 非空, 则从 \(S\) 种选出一个元素 \(x\), 包含 \(x\) 的集合 \(T\) 和不包含 \(x\) 的集合 \(T\) 一一对应且贡献相反。

\(f'_S = \sum\limits_{T} (-1)^{|S\cap T|}f_T\), 称为 \(f\) 的沃尔什变换,也称为 \(\operatorname{FWT}(F)\)

\(f_S = \dfrac{1}{2^n} \sum\limits_{T} (-1)^{|S\cap T|}f'_T\), 称为 \(f\) 的沃尔什逆变换, 称为 \(\operatorname{IFWT(F)}\)

\(f_S = \dfrac{1}{2^n}\sum\limits_T (-1)^{|S \cap T|}\sum\limits_U (-1)^{|T \cap U|}f_U\)

$=\sum\limits_{U} f_U \dfrac{1}{2^n}\sum\limits_T (-1)^{|(S \oplus U) \cap T| } = \sum\limits_{U}f_U\dfrac{1}{2^n} [S \oplus U = \emptyset] $

\(=f_S\)

考虑如何计算沃尔什变换,这里我们换一种思路, 考虑分治。

方便起见,我们将 \(\operatorname{popcount}(i \operatorname{and}k)\) 的奇偶性记作 \(i \odot k\)

计算 \(f_i\) 的沃尔什变换时, 设 \(f = f^-+x^n \cdot f^+\)

\(i\) 的第 \(n\) 位为 \(0\) :

\(f'_i =(\sum\limits_{0 \leq j <2^{n-1}}f_j \times (-1)^{i \odot j})+(\sum\limits_{2^{n-1}\leq j <2^n}f_j \times (-1)^{i \odot j})\)

\(f'_i =(\sum\limits_{0 \leq j <2^{n-1}}f_j \times (-1)^{i \odot j})+(\sum\limits_{2^{n-1}\leq j <2^n}f_j \times (-1)^{i \odot (j-2^{n-1})})\)

\(=f'^-_j+f'^+_j\)

同理,当 \(i\) 的第 \(n\) 位为 \(1\) 时 : \(f'_i = f'^-_j-f'^+_j\)

部分代码:

inline void FWT_xor(int *f, int opt) {
    for(int i = 1; i < n; i <<= 1) 
	    for(int j = 0; j < n; j += (i << 1)) 
		    for(int k = 0; k < i; ++k) {
		    	const int p = f[j + k], q = f[j + k + i]; 
		    	f[j + k] = 1ll * (p + q) * opt % mod, f[j + k + i] = 1ll * (p - q) * opt % mod; 
		}
}

4.快速沃尔什变换

将从线性代数的角度来解释快速沃尔什变换的原理。

4.1 核心思想

根据 DFT, 化乘积为卷积, 考虑对集合幂级数进行线性变换 \(T\), 设矩阵为 \(C\), 即 \(f'_i=\sum\limits_{j}C_{i,j}f_j\)

作为这个线性变换,我们要求 \(C(i,j)C(i,k)=C(i,j \oplus k)\),这是因为 :

\(h'_i=\sum\limits_{j}C_{i,j}h_j\)

\(=\sum\limits_{j}C_{i,j}\sum\limits_{k=0}\sum\limits_{l=0}[k \oplus l = j]f_kg_l\)

\(=\sum\limits_{k=0}\sum\limits_{l=0}C_{i,k \oplus l}f_kg_l\)

\(h'_i=f'_ig'_i\)

\(=(\sum\limits_{k}C_{i,k}f_k)(\sum\limits_{l}C_{i,l}g_l)\)

\(=\sum\limits_{k=0}\sum\limits_{l=0}C_{i,k}C_{i,l}f_kg_l\)

\(C(i,j)C(i,k)=C(i,j \oplus k)\), 考虑位运算中各位独立,将 \(i,j\) 二进制分解,则有 :

\(C(i,j)=\prod c(i_p,j_p)\) ,从这点出发,可以反推出 \(c\) 的具体值。

  • 注意, 矩阵 \(c\) 要存在逆元,否则没有逆变换。

4.2 或卷积

当 $\oplus $ 为按位或时,如下构造 :

  • \(0 \or 0 = 0\), 因此 \(c_{0,0}c_{0,0}=c_{0,0}\),且 \(c_{1,0}c_{1,0}=c_{1,0}\) ,说明 \(c_{0,0}\)\(c_{1,0}\) 等于 \(0\)\(1\),但不能都为 \(0\),否则无逆。
  • \(0 \or 1 = 1\)\(1 \or 0 = 1\),因此 \(c_{0,0}c_{0,1}=c_{0,1}\),且 \(c_{1,0}c_{1,1}=c_{1,1}\)
  • \(1 \or 1 = 1\),因此 \(c_{0,1}c_{0,1}=c_{0,1}\),且 \(c_{1,1}c_{1,1}=c_{1,1}\)

容易枚举得到所有的 \(c\)\(\begin{bmatrix} 1, 1 \\ 1, 0 \end{bmatrix}\)\(\begin{bmatrix} 1, 0 \\ 1, 1 \end{bmatrix}\),互为转置。

对应的逆矩阵为 \(\begin{bmatrix} 0, 1 \\ -1, 1 \end{bmatrix}\)\(\begin{bmatrix} 1, 0 \\ -1, 1 \end{bmatrix}\),后者相当于求子集和的逆变换。

对于与卷积同理,可自行推导,最后的 \(c\) 矩阵为 \(\begin{bmatrix} 1 \ \ \ 1 \\ 0 \ \ \ 1 \end{bmatrix}\)

4.3 异或卷积

当 $\oplus $ 是按位异或时,如下构造 :

  • 对于任意 \(i,j\),均有 \(c_{i,0}c_{i,j}=c_{i,j}\), 所以 \(c_{0,0}=c_{1,0} = 1\)
  • \(0 \oplus 1 = 1\),因此 \(c_{0,0}c_{0,1}=c_{0,1}\),且 \(c_{1,0}c_{1,1}=c_{1,1}\)
  • \(1 \oplus 1 = 0\),因此 \(c_{0,1}c_{0,1}=c_{0,0}\),且 \(c_{1,1}c_{1,1}=c_{1,0}\),说明 \(c_{0,1}\)\(c_{1,1}\)\(-1, 1\)

枚举得到所有的 \(c\)\(\begin{bmatrix}1 -1 \\ 1 \ \ \ \ 1 \end{bmatrix}\)\(\begin{bmatrix} 1 \ \ \ \ 1 \\ 1 \ -1 \end{bmatrix}\),其中第二个矩阵与沃尔什变换相等。

对应的逆矩阵为 \(\begin{bmatrix} 0.5 \ \ 0.5 \\ 0.5 -0.5 \end{bmatrix}\)

5.子集卷积

5.1 定义

将集合之间的二元运算定义为不相交集合并,即:

\(h_S=\sum\limits_{L}\sum\limits_{R}[L \cup R =S][L \cap R = \emptyset]f_Lg_R\)

注意到 \(L \cup R = S\) 时, \(L \cap R = \emptyset\) 的充要条件为 \(|L| +|R| =|S|\)

\(f_{i,S} = [|S| = i]f_S\)\(g\) 同理, \(H_i = \sum\limits_{j+k=i}f_j \cdot g_k\) 。根据性质有 \(h_S = H_{|S|,S}\)

将等式两边同时取 FMT, 得到 \(H'_i = \sum\limits_{j+k=i}f'_i \cdot g'_j\)

因此,首先 \(O(n^22^n)\) 计算出每个 \(f_j\)\(g_k\) 的莫比乌斯变换, 然后 \(O(n^22^n)\) 按柿子计算出 \(H'_i\),i最后将每个 \(H’_i\) 逆变换即可。

【模板】子集卷积代码如下:

#include <bits/stdc++.h> 

const int INF = 0x3f3f3f3f; 
const int mod = 1e9 + 9; 

typedef long long ll; 
typedef unsigned long long ull; 
typedef double db; 

int n, len; 
int pc[(1 << 20)]; 
int f[21][(1 << 20)], g[21][(1 << 20)]; 
int h[21][(1 << 20)]; 

inline void FWT_or(int *a, int n, int op) {
	for(int i = 1; i < n; i <<= 1) 
	    for(int j = 0; j < n; j += (i << 1)) 
	        for(int k = 0; k < i; ++k) a[j + k + i] = (a[j + k + i] + (op == 1 ? a[j + k] : mod - a[j + k])) % mod; 
}

inline int read() {
	int f = 1, s = 0; char ch = getchar(); 
	while(!isdigit(ch)) (ch == '-') && (f = -1), ch = getchar(); 
	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); 
	return f * s; 
}

int main() 
{
	n = read(), len = n, n = (1 << n); 
	for(int i = 1; i < n; ++i) pc[i] = pc[i >> 1] + (i & 1); 
	for(int i = 0; i < n; ++i) f[pc[i]][i] = read(); 
	for(int i = 0; i < n; ++i) g[pc[i]][i] = read(); 
	for(int i = 0; i <= len; ++i) FWT_or(f[i], n, 1), FWT_or(g[i], n, 1); 
	for(int i = 0; i <= len; ++i) 
	    for(int j = 0; j <= i; ++j) 
	        for(int k = 0; k < n; ++k) h[i][k] = (h[i][k] + 1ll * f[j][k] * g[i - j][k] % mod) % mod; 
	for(int i = 0; i <= len; ++i) FWT_or(h[i], n, -1); 
	for(int i = 0; i < n; ++i) printf("%d ", h[pc[i]][i]); 
	return 0; 
}

6.例题

1. CF1530F Bingo

题意 : \(n \times n\) 的表格, 表格每个数有 $p_{i,j} $ 的概率为 \(1\),否则为 \(0\)

求至少有一行或一列或一条对角线全为 \(1\) 的概率, 对角线指主对角线或副对角线。 \(n \leq 21\)

\(\operatorname{solution}:\)

把两条对角线看成特殊的行, 首先先转化为 \(1 -\) 所有行 / 列都不全为 \(1\) 的概率。

容斥,钦定每一列是否全为 \(1\) ,则贡献为 \((-1)^i\) ,这样就限制了每一行的选择方案,但不能全为 \(1\)

所以直接按每一行转移就完了啊?用什么 FWT ?

2.[ARC100C] Or Plus Max

题意 :一个长度为 \(2^n\) 的序列, 对于每一个 \(1 \leq K \leq 2^n - 1\),找出最大的 \(a_i+a_j (i \operatorname{or}j \leq K)\) 并输出。 \(n \leq 20\)

\(\operatorname{solution}:\)

高维前缀和模板题 ?直接对于每一个状态 \(S\), 记录它的最大值和次大值, 转移时合并即可。

3.P4221 [WC2018]州区划分

题意 : \(n\) 个点, \(m\) 条边的无向图和一个常数 \(p\)。将 \(n\) 个点划分成若干个洲, 满足每个洲都不含欧拉回路,定义这个洲的权值为 \((\dfrac{\sum\limits_{x \in V_i}w_x}{\sum\limits_{j=1}^{i}\sum\limits_{x \in V_j}w_x})^p\), 一个划分的权值为所有洲权值的乘积, 求所有划分的权值和。 \(n\leq 21, m \leq 400\)

\(\operatorname{solution}:\)

dp 方程都写你脸上了,设 \(f_S\) 表示已选点集合为 \(S\) 的总权值, 则 \(f_S = \sum\limits_{T \subset S}f_T(\dfrac{W_{S/T}}{W_S})^p\)

\(f_S = \dfrac{1}{W_S^p}\sum\limits_{T \subset S}f_TW_{S/T}\), 标准的子集卷积的形式。

因为 \(f_S\) 要从比它小的 \(f_T\) 转移, 所以要从 \(len\) 从小往大转移, 每次更新完当前的 \(f\) 值后要赶快 FWT 过去。

4.CF914G Sum the Fibonacci

题意 :定义五元组 \((a,b,c,d,e)\) 满足条件,当且仅当\((s_a \operatorname{or} s_b) \operatorname{and}s_c\operatorname{and} (s_d \operatorname{xor}s_e)=2^i, i \in Z\),且 \(s_a \operatorname{and}s_b = 0\), 定义权值为 \(f(s_a \operatorname{or} s_b)f(s_c)f(s_d\operatorname{xor}s_e)\), 求所有满足条件的五元组的和。 \(n \leq10^6, s_i < 2^{17}\)

\(\operatorname{solution}:\)

\(\operatorname{cnt}_x\)\(x\) 的出现次数,\(A = s_a \operatorname{or} s_b\)\(B = s_c\)\(c = s_d \operatorname{xor} s_e\)。其中 \(A\)\(C\) 可以 FWT 得到。

最后把 \(f_A \times cnt_A\)\(f_b \times cnt_B\)\(f_c \times cnt_C\) 做一遍 and 卷积即可。

5.CF662C Binary Table

题意 : \(n \times m\) 的表格, 每个数为 \(0 / 1\),每次可以翻转一行或一列,求若干次操作后,表格中最少有多少个 \(1\)\(n \leq 20, m \leq 10^5\)

\(\operatorname{solution}:\)

发现 \(n\) 很小,于是考虑枚举每一行翻 / 不翻,设状态为 S。

再枚举列,此时行的状态已经固定了,考虑预处理一个 \(g_S\),表示 \(S\) 状态 翻 / 不翻能得到的最少的 1。

于是此列对答案的贡献为 \(g_{now \operatorname{xor}S}\),答案就是所有列的和。

发现本质不同的列只有 \(2^n\),这引导我们记下每个列状态 \(S\) 的个数,设当前行状态为 \(S\),对答案的贡献是 : \(ans_S = \sum\limits_{T=0}^{2^n-1}cnt_T \times g_{T \operatorname{xor}S}\) ,是一个 \(\operatorname{xor}\) 卷积的形式,于是直接 FWT。

最后对于每个 \(S\) 取个 \(\min\) 即可。

posted @ 2022-03-25 19:19  henrici3106  阅读(337)  评论(0编辑  收藏  举报