受这几天看到的不少高维前缀和题目的影响,我决定系统地学习一下集合幂级数的一套理论了。内容主要来自2015年吕凯风(VFleaKing)国家集训队论文《集合幂级数的性质与应用及其快速算法》(pdf版本会放在附录里),包括集合并卷积、集合对称差卷积、子集卷积、快速莫比乌斯变换、快速莫比乌斯反演、快速沃尔什变换及逆变换等,以及附带进行的一些练习。
(UPD 2021.4.20) 推荐观看:Fourier Analysis of Boolean functions || @ CMU || Lecture 8a of CS Theory Toolkit
[TODO] 把练习第一题搞清楚
[TODO] 把第三道论文题学会
引言及定义
类似数列的生成函数,对于集合,我们引入集合幂级数来解决一些有关集合的动态规划问题。
定义:设 $F$ 是一个域,则称函数 $f:2^U\to F$ 是 F 上的一个形式幂级数,记 $f_S=f(S)$ 为集合幂级数第 $S$ 项的系数。
我们记为:
$$
f=\sum_{S\subseteq 2^U} f_S x^S
$$
显然可以定义集合幂级数的加法:$(f+g)(S) = f(S)+g(S)$ ,减法也类似。这两种运算都是 $O(2^n)$ 的。
如何定义乘法?为了保证乘法对加法的分配律,若 $h=f\cdot g$ ,我们应该有:
$$
\sum_{S\in 2^U} h_S x^S = \big(\sum_{L\in2^U}f_Lx^L\big) \cdot (\sum_{R\in 2^U} g_R x^R) = \sum_{L\in 2^U} \sum_{R\in 2^U} (f_L x^L) \cdot (g_R x^R)
$$
因此我们只需要规定 $(f_L x^L) \cdot (g_R x^R)$ 的运算结果,我们希望它是以某种集合运算乘起来的。我们设一个 $2^U$ 中满足交换律、结合律、空集是单位元的二元运算 $\ast$ ,那么我们就可以定义 $(f_L x^L) \cdot (g_R x^R) = (f_L g_R) x^{L\ast R}$ 。至此集合幂级数形成了一个交换环,并且包含了整个 $F$ 作为子环。
常见定义 $\ast$ 为集合并运算、集合对称差运算以及子集卷积,我们一一解决。
集合并卷积(FWT_or)
原理
即给出两个集合幂级数 $f,g$ ,求 $h = f\ast g$ 满足:
$$
h_S = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\cup R = S] f_L g_R \tag{1}
$$
怎么做呢?暴力做是 $O(4^n)$ 的,一种做法是分治乘法,可是总感觉在理论价值上面会逊色一些。有没有类似 FFT 的一种变换,使得变换后将卷积直接变成点乘呢?莫比乌斯变换做到了这一点。
我们定义集合幂级数 $f$ 的莫比乌斯变换 $\hat{f} $为:
$$
\hat{f_S} = \sum_{T\subseteq S} f_T
$$
反过来,如何求莫比乌斯逆变换呢(常常称为莫比乌斯反演)?有关反演看这里:《炫酷反演魔术》。很容易由容斥原理,我们又有:(这一步建议在纸上推导一下,要不然可能会觉得不直观)
$$
f_S = \sum_{T\subseteq S} (-1)^{|S|-|T|} \hat{f_T}
$$
现在,我们可以解决 (1) 中的问题了。对 (1) 式的左右两边同时做莫比乌斯变换
$$
\begin{aligned}
\hat{h_S} & = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\cup R \subseteq S] f_L g_R\
& = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\subseteq S][ R \subseteq S] f_L g_R\
& = (\sum_{L\subseteq S}f_L) (\sum_{R\subseteq S}g_R)\
& = \hat{f_S}\hat{g_S}
\end{aligned}
$$
因此,我们想要的性质是成立的。只需要把 $f,g$ 做莫比乌斯变换,点乘起来,然后再做莫比乌斯反演即可得到集合并卷积。
如何进行莫比乌斯变换呢?可以使用递推,设 $\hat f^{(i)}S$ 表示只考虑 $S\oplus T\subseteq {1,\cdots,i}$ 的子集 $T$ 时的莫比乌斯变换第 $S$ 项,令 $\hat f_S^{(0)} = f_S$ ,那么对于每个不包含 $i$ 的 $S$ 有:
$$
\hat{f_S^{(i)}} = \hat{f_S}^{(i-1)}\
\hat{f}{S\cup{i}} ^{(i)} = \hat f_{S\cup{i}}^{(i-1)} + \hat f _S ^{(i-1)}
$$
由此递推计算即可。复杂度 $O(n2^n)$
实现
代码非常简短,dmt
变量为 1 时表示正变换,为 -1 时表示逆变换。
1 | vector<int> fmt_or(vector<int> A, int dmt = 1) { |
集合交卷积(FWT_and)
由于 $S\cap T = C_U(\bar S \cup \bar T)$,因此将S、T翻转之后,可以直接用FWT_or导出。可是实际上还有更简洁的实现,如下面代码所示。
复杂度 $O(n2^n)$
1 | vector<int> fmt_and(vector<int> A, int dmt = 1) { |
集合对称差卷积(FWT_xor)
原理
这一回把 $\ast$ 定义为集合对称差。即给出两个集合幂级数 $f,g$ 求 $h = f\ast g$ :
$$
h_S = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\oplus R=S] f_L g_R \tag 2
$$
分治乘法在这里也是可行的,但是对应的变换方法是什么呢?这次我们使用快速沃尔什变换及其逆变换(本质上是高维 FFT)。
快速沃尔什变换:
$$
\hat{f_S} = \sum_{T\subseteq 2^U} f_T (-1) ^{|S\cap T|}
$$
其逆变换为:(我没有找到靠谱证明)
$$
f_S = \frac {1}{2^n}\sum_{T\subseteq 2^U} \hat{f_T}(-1)^{|S\cap T|}
$$
沃尔什变换是具体如何解决(2)式的问题的呢?我们基于下面的事实:
$$
\frac {1}{2^n}\sum_{T\subseteq 2^U} (-1)^{|S\cap T|} = [S=\oslash]
$$
化简过程如下:
$$
\begin{aligned}
h_S & = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\oplus R\oplus S = \oslash] f_L g_R\
& = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} \frac{1}{2^n} \sum_{T\subseteq 2^n} (-1)^{|S\cap (L\oplus R \oplus S)|} f_l g_R\
& = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} \frac{1}{2^n} \sum_{T\subseteq 2^n} (-1)^{|T\cap L|} (-1)^{|T\cap R|}(-1)^{|T\cap S|} f_l g_R\
& = \frac{1}{2^n} \sum_{T\subseteq 2^n} (-1)^{|T\cap S|} \Big(\sum_{L\subseteq 2^U} (-1)^{|T\cap L|} f_L\Big)
\Big(\sum_{R\subseteq 2^U} (-1)^{|T\cap R|} g_R\Big)\
& = \frac{1}{2^n} \sum_{L\subseteq 2^U} (-1)^{|L\cap S|} \hat{f_S} \hat{g_S}
\end{aligned}
$$
即: $\hat{h_S} = \hat{f_S} \cdot \hat{g_S}$
注:在理论上,这种变换还有一个小问题,就是特征为2的 $F$ 上上述算法会失效,因为上述算法涉及乘以 $\frac {1}{2^n}$ 。不过一般情况下不会成问题。
接下来考虑如何计算快速沃尔什变换。依然可以使用递推,设 $\hat f^{(i)}S$ 表示只考虑 $S\oplus T\subseteq {1,\cdots,i}$ 的子集 $T$ 时的沃尔什变换第 $S$ 项,令 $\hat f_S^{(0)} = f_S$ ,那么对于每个不包含 $i$ 的 $S$ 有:
$$
\hat{f_S^{(i)}} = \hat{f_S}^{(i-1)}+\hat{f}{S\cup{i}}^{(i-1)}\
\hat{f}_{S\cup{i}} ^{(i)} = \hat f S ^{(i-1)} - \hat f{S\cup{i}}^{(i-1)}
$$
由此递推计算即可。复杂度 $O(n2^n)$
实现
1 | vector<int> fwt_xor(vector<int> A, int dmt = 1) { |
子集卷积
这一部分感觉论文讲得有点自闭,推荐 Dance Of Faith 的这篇博客 。
原理
考虑这种形式的卷积如何处理:
$$
h_S = \sum_{T\subseteq S} f_T g_{S-T} \tag 3
$$
实际上,这种卷积等价于定义 $\ast$ 为不相交集合的并,即
$$
L \ast R =
\begin{cases}
\varnothing, & L \cap R \neq \varnothing \
L \cup R, & \text{otherwise}
\end{cases}
$$
我们稍微改写一下 (3) 式:
$$
\begin{aligned}
h_S & = \sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [L\cap R = \varnothing] [L\cup R = S] f_L g_R \
& =\sum_{L\subseteq 2^U} \sum_{R\subseteq 2^U} [|L| + |R| = |S|][L\cup R = S] f_L g_R
\end{aligned}
$$
我们只需要处理掉 $|L|+|R| = |S|$ 这一项。实际上我们直接加一维表示集合的大小,暴力处理即可。
具体来讲,初始时,我们先只把 $f_{pc(S),S}$ (pc(S)
表示 popcount
,即 S 二进制表示中有多少位是 1 )的值赋成原来的 $f_{S}$($g$ 也使用同样的操作),然后对每一个 $f_i$ 做一遍FMT,点值相乘时这么卷积:$h_{i, S} = \sum\limits_{j = 0}^{i} f_{j,S} * g_{i - j, S}$ 。代码很简洁,如下所示:
1 | vector<int> subset_conv(vector<int> A, vector<int> B) { |
小结
FWT_OR
1 | if FMT : f[S|(1<<i)] += f[S] |
FWT_AND
1 | if FMT : f[S] += f[S|(1<<i)] |
FWT_XOR:
1 | l = f[S], r = f[S|(1<<i)] |
子集卷积:
1 | for i in range(0, n + 1): |
完整板子代码如下,由于没有采用类似 FFT 蝴蝶操作的优化,效率不是很高,在LOJ的子集卷积模板题上跑了 4500/5000 ms
。Luogu 4717
1 |
|
练习
[HAOI2015] 按位或
Description
刚开始你有一个数字0,每一秒钟你会随机选择一个 $[0,2^n-1]$ 的数字,与你手上的数字进行按位或操作。选择数字 $i$ 的概率是 $p_i$。保证 $0\leq p_i \leq1$,$\sum p_i = 1$。问期望多少秒后,你手上的数字变成 $2^n-1$。无穷输出 INF
数据范围:$n\leq 20$
Source: BZOJ4036 vfk论文第一道例题。
Solution
留坑。
Day8 I. 岸边露伴的人生经验
Description
岸边露伴是一个天才漫画家,他经常用自己的替身天堂之门来查看别人的人生经历,为自己的漫画积累素材。最近他学会了将一个人的人生经历编码成一个 $10$ 维的向量,每一维取值为 ${0,1,2}$ 中的一个元素。定义向量$\overrightarrow{V}=(x_1,x_2,\cdots,x_{10})$ 的模长 $|\overrightarrow{V}|$ 为 $\sqrt{x_1^2+x_2^2+\cdots+x_{10}^2}$。令第 $i$ 个人的人生经历对应的向量为$\overrightarrow{V_i}$,则第ii个人和第 $j$ 个人的人生轨迹的差别可以用 $|\overrightarrow{V_i}-\overrightarrow{V_j}|$ 衡量。岸边露伴收集了 $n$ 个人的向量,他想要知道这些人里,人生轨迹差别相同的二元组有多少对,即有多少个四元组 $(i,j,k,l)(1 \le i,j,k,l \le n)$ 满足 $|\overrightarrow{V_i}-\overrightarrow{V_j}|=|\overrightarrow{V_k}-\overrightarrow{V_l}|$
数据范围:$n\leq 10^5$
Source: CCPC-Wannafly Winter Camp Day8 (Div1, onsite) Day 8 Problem I
Solution
考虑每个向量取值只有 ${0,1}$ 的情况,每个人的向量为一个二进制数,装进桶里,直接进行自己卷积自己的异或 FWT ,这时集合幂级数的每一项都代表这个集合被异或出来的 可能方式。再扫一遍每一个状态,按照 bitcount
统计距离,加入另一个数组,平方一下即可。
考虑这道题,多了一个 $2$ ,我们只需要把每一个向量中一个数位拆成两个字符即可,将 1
变为 01
,2 变为 10
, 0
变为 00
,然后把上一种做法求 bitcount
统计距离的方式稍微改一下就可以了。
Code
1 | /* Generated by powerful Codeforces Tool |
CF1119 H. 难题,精妙的 FWT
Description
给定 $a,b,c$ ,以及 $n$ 个 $k$ 位 bitmask $A_i,B_i,C_i$ ,求下面 $n$ 个集合幂级数的异或 FWT,即:
$$
\prod_{1\leq i\leq n} (ax^{A_i}+bx^{B_i}+cx^{C_i})
$$
数据范围:$n\leq 10^5,k\leq 17,0\leq A_i,B_i,C_i < 2^k,$
Source: Codeforces Global Round 2 Problem H
Solution
按照正常的 FWT 思路,就是把这 $n$ 个集合幂级数分别求出 FWT,然后再点乘起来,然后再做 IFWT。复杂度是 $O(nk2^k)$ 的,无法通过此题。
但是这道题需要注意的一点是每一个集合幂级数都只有三项,并且 $a,b,c$ 是固定的。回顾 FWT 的公式:
$$
\hat{f}(S) = \sum_{T\subseteq 2^U} f(T) (-1)^{|S\cap T|}
$$
因此每个集合幂级数 FWT 之后也仅仅会有 $\pm a \pm b \pm c$ 这么 8 种项。为了使问题更加简单,我们进行这样的变换: $B_i:=B_i \oplus A_i,C_i := C_i \oplus A_i, A_i := 0$ 。变换之后我们得出的结果的第 $S$ 项就等价于答案的第 $S\oplus xorsum$ 项,其中 $xorsum = \oplus_{i=1}^n A_i$。可以发现,这样做之后只剩下 $a\pm b \pm c$ 这四项了。
我们的一个重要观察是,将 FWT 之后的这 $n$ 个集合幂级数乘起来的结果中,对于一个固定的位置 $S$ ,这一项的系数一定是 $(a+b+c)^x (a+b-c)^y (a-b+c)^z (a-b-c)^w$ 的形式。如果我们能够把 $x,y,z,w$ 解出来,整个问题就解决了。首先,我们有一个最朴素的关系式:
$$
x+y+z+w = n
$$
之后有两种说法,一种是官方题解所说的考虑所有 $n$ 个 FWT 之后的幂级数的第 $S$ 项之和,然后再多考虑一个条件即可得到四个方程;另一种则非常简洁、对称、优美,可是我对这种方法还没有简洁优美的证明。这种方法是这样的:
- 将 $A_i\oplus B_i$ 的值统计入一个数组 $f$,对 $f$ FWT之后,对于其第 $S$ 项有:$x+y-z-w=f[S]$。
- 将 $A_i\oplus C_i$ 的值统计入一个数组 $g$,对 $g$ FWT之后,对于其第 $S$ 项有:$x-y+z-w=g[S]$。
- 将 $B_i\oplus C_i$ 的值统计入一个数组 $h$,对 $h$ FWT之后,对于其第 $S$ 项有:$x-y-z+w=h[S]$。
然后消元法即可。
Code
1 | /* Generated by powerful Codeforces Tool |
CF662C 超级好题
Description
你有一个 $n$ 行 $m$ 列的 01 矩阵,你可以进行翻转行与翻转列两种操作(翻转即 0 变 1,1 变 0)任意多次,问最后得到的矩阵的 1 的个数最少是多少。
数据范围:$n\leq 20,m\leq 100000$
Source: CROC 2016 - Final Round Problem C. Binary Table
Solution
注意 $n\leq 20$ 的条件。先考虑 $O(m2^n )$ 暴力怎么做。记第 $j$ 列的数的 bitmask 为 $A_j$ ,我们枚举行的翻转状态的 bitmask为 $S$ ,然后对于每一种状态 $S$,我们查看每一列,第 $j$ 列被变为了 $A_j \oplus S$。考虑这一列是否翻转,显见答案增加了 $\min{pop(A_j \oplus S), n-pos(A_J\oplus S)}$,其中 $pop(S)$ 是指 $S$ 二进制表示中 1 的个数。我们记$f(S) = \min{pop(S),n-pop(S)}$ ,那么对于行的枚举状态 $S$ 我们得到的答案就是这个:
$$
\sum_{i=1}^m f(S\oplus A_i)
$$
由于 $A_j$ 的值域也是 $2^n$,我们记 $A$ 中 $x$ 这个值出现的次数为 $c(x)$,那么上式可以改写为:
$$
\sum_{T \subseteq 2^n} f(T \oplus S) \cdot c(T)
$$
是不是有点 FWT 的形式了?进一步地:
$$
\begin{aligned}
ans(S) &= \sum_{T \oplus U = S} f(T) c(U)
\end{aligned}
$$
这不正是 FWT 吗?于是我们求出 $f$ 与 $c$ 的异或 FWT,然后扫一边 ans 数组找最小值即可。
Code
1 |
|
[HDU5909]
[WC2018]州区划分