FWT 小记

· · 算法·理论

FWT 小记

原问题

我们想做 c_i = \sum\limits_{j \oplus k = i}a_jb_k,其中 \oplus 是一种位运算。

基础思路

沿用 FFT 和 NTT 的思路,我们希望通过【可接受的复杂度的】线性变换得到(点值表达)A、B、C,使得点乘可得 C_i = A_i \cdot B_i,这样我们就能再变换回 c,也就完成了位运算卷积。

这种变换就是快速沃尔什变换(FWT),即 FWT(a) = A。我们称正向(a \rightarrow A,系数变点值)的过程为 FWT,逆向(A \rightarrow a,点值变系数)为 IFWT。(也有叫 DWT 和 IDWT 的,但惯用 FWT 了,能看懂就行 qwq)

我们设贡献系数 coe(i,j),意为 A_i = \sum \limits_{j} coe(i, j)a_j,将其与 c_i = \sum\limits_{j \oplus k = i}a_jb_kC_i = A_i \cdot B_i 联立,可以解得:对于 coe(i,j),我们要求 coe(i,j)coe(i, k) = coe(i,j \oplus k)

我们只需要构造这个系数即可。又由于位运算每一位独立,所以可以按位做,也就是我们 coe(i,j) 只需要对独立的一位(也就是空间中的一维)构造,这保证了比较优秀的复杂度。

其他理解

还有其他理解方式:

这里采用矩阵的思路,注意到上面写的是 “位运算下” 而非 “二进制位运算下”,所以他是极具扩展性的。我们通常称这个矩阵为位矩阵。

具体实现

直接给出二进制或、与、异或运算的 FWT 矩阵和 IFWT 矩阵。

或: \begin{bmatrix} 1 & 0 \\ 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \\ -1 & 1 \end{bmatrix} ,其分别对应了高维缀和、缀差分,二进制下前者就是子集和。

与: \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & -1 \\ 0 & 1 \end{bmatrix} ,其分别对应了高维缀和、缀差分,二进制下前者就是超集和。

异或: \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \begin{bmatrix} 0.5 & 0.5 \\ 0.5 & -0.5 \end{bmatrix}组合意义天地灭代数推导保平安

给出代码实现。看似三个循环其实就是在对每一位分别做,写成类高维前缀和的形式也是对的。

void fwt(int *f, int len, bool op){
    for (int k = 1; k < len; k <<= 1)
        for (int i = 0; i < len; i += k + k)
            for (int j = 0; j < k; ++j)if (op){
                f[i + j + k] = (f[i + j + k] + f[i + j]) % mod;

                f[i + j] = (f[i + j] + f[i + j + k]) % mod;

                int x = f[i + j], y = f[i + j + k];
                f[i + j] = (x + y) % mod, f[i + j + k] = (x - y) % mod;
            }
            else {
                f[i + j + k] = (f[i + j + k] - f[i + j]) % mod;

                f[i + j] = (f[i + j] - f[i + j + k]) % mod;

                int x = f[i + j], y = f[i + j + k];
                f[i + j] = (ll)(x + y) * inv2 % mod,
                f[i + j + k] = (ll)(x - y) * inv2 % mod;
            }
}

简单性质

由于 FWT 是一个线性变换,所以显然有加法和数乘的性质,即有:

k 进制扩展

然后我们拓展到 k 进制下。

每一位取 \max,位矩阵是左下 1 矩阵,逆矩阵为单位矩阵和左下相邻是 -1 的矩阵。

每一位取 \min,位矩阵是右上 1 矩阵,逆矩阵为单位矩阵和右上相邻是 -1 的矩阵。

每一位不进位加法,单位根启动了,因为要有逆,直接拉范德蒙德矩阵,这个可能个比较常用。

单位根模不了就启动~~小圆多项式~~分圆多项式,感觉开始万泉部诗人了。 ## 其他参考 更详细内容:[cmd 位运算卷积](https://www.luogu.com.cn/article/y0unggsj) 以及 [ZnPdCo 快速沃尔什变换](https://znpdco.github.io/blog/2024/05/07/FWT/) 。【基础思路】中我省略的推式子 cmd 有,而两者都有更详细的 $k$ 进制扩展。没点式子大蛇别搁那 $n / 2$ 搞来搞去了,不如就用按位的思想去考虑。 ## 例题选做 注意到这是一个做题记录或者题单,下面的内容只是要点而非题解。按时间排序,难度显然与顺序无关。 ### [P4717 模板 FWT](https://www.luogu.com.cn/problem/P4717) 食用模板先。 ### [P6097 子集卷积](https://www.luogu.com.cn/problem/P6097) 直接真的子集卷积是 $3^n,n=20$ 不可接受,但我们子集写成 $i \& j = 0, \ i | j = k$,后一项或可以直接当成 FWT 做,而前一项要求不交相当于要求 $ppc(i)+ppc(j) = ppc(k)$,这里 $ppc$ 是集合大小或者说二进制一的个数。 那直接按照 $ppc$ 分成 $n$ 类然后暴力把 $ppc(i)+ppc(j) = ppc(k)$ 的 FWT 数组点乘起来贡献就行,复杂度 $O(n^2 2^n)$。 ### [CF449D Jzzhu and Numbers](https://www.luogu.com.cn/problem/CF449D) cmd 博客里说的基于性质的题。 看题解区和 cmd,跟 FWT 有关的有两种写法。他们唯一的区别在于 $2^{f_i}$ 和 $2^{f_i}-1$。这仅仅来自于求出 $f_i$ 表示与和为 $i$ 的方案数时的思路的不同。 对于前者,初始思路为 $n$ 次 FWT 卷积,再观察到每次卷的只有 $f_{msk} = f_{a_i} = 1$,进而转化为二的次幂,所以只要一次 FWT 即可。但由于是与卷积,所以我们令初始空集的与值为全集 $msk$,所以这个算法其实把选出一个空集也算进去了,所以可能在 $a_i=0$ 时会出错,但其实我们只要在最后把空集减掉即 $f_{msk} \leftarrow f_{msk}-1$ 即可。 对于后者,思路来自于 DP,或者说思路就来自于 FWT 的构造。FWT 变换后 $f_i$ 是 $i$ 的超集和,也就是 $A_i = \sum_{i\in j} a_j$,这一题里就是是 $i$ 超集的数的个数,那自然地 $B_i = 2^{A_i} - 1$ 即为选出一些数是的与起来是 $i$ 的超集的方案数,而这东西就是我们要求的东西的 FWT 变换后结果。再做一遍 IFWT 即可。 牛啊。 *** 插播一段,第二篇博客的哥们在博客里发了一个他的 duel 记录,一看诶咋都是两千的,然后他还有个 2200 的不会,我点开一看诶这不是我们期望随便微分一下转面积随便 $n^2$ dp 吗遂写了一下~~想当年在 nfls 模拟赛上手动搁那小量手搓微分啊~~。再往后看诶这不是我们联考的题吗,一看还真是 11.27 的 NOIP 模拟赛,一眼看到那个我当时还搁那二项式反演后来发现拆贡献可以撤销背包算中位数期望的题。哦还是我们联考的哥们,这么亲切?先奶一口是个初三初二、停课早、然后学多项式的天赋哥。 *** ### [CF1119H Triple](https://www.luogu.com.cn/problem/CF1119H) 我天这题怎么直接神了啊。 先是 $n$ 个多项式异或卷积,然后每个里面只有三个位置有值,然后使用异或卷积的组合意义 $-1^{ppc(i \& j)}$,发现 $x,y,z$ 的贡献系数只有 $2^3$ 种,再用 trick (异或的自反性)把 $a、b、c$ 都异或上 $a$,这样实际上是把答案序列进行了异或 $\oplus_{i}a_i$ 的变换,这是可以最后处理的。那就变成 $0、b \oplus a、c \oplus a$,只有后两项的贡献需要讨论的四种情况,然后你整点特殊值(其实是有构造思路的)整出四个方程搁那手动解一下,答案就水灵灵地出来了? cmd 写得通俗易懂,魏老师写得更形式化。魏老师其实是把三个数扩展到了 $m$ 个数,也就是有 $2^m$ 种系数组合,构造 $2^m$ 个方程我们就对于 $T \in \{0, 1,...,m\}$,FWT 时仅带入 $\oplus_{j\in T}a_j$ 位置值为 $1$ 就能构造(充分利用 FWT 的美好性质)。 神了啊。遇到 FWT 卷积但是有值的位比较少的情况,考虑构造方程计算每种系数出现次数。 *** 再插播一段。给 USACO 铂金创飞了。主要是说美耶润耶找 USACO 陪练带打出价很好然后给我找了个三千块的活(当时我:不是很想打 USACO,fxj:三千,我:哦那啥时候打?~~哎码真香~~),然后三题搁那硬刚三小时各种 FWT 可持久化平衡树冲正解全不会直接倒闭部分分都没怎么想。最后狂用英语试图跟对方解释。不过他应该也倒闭了看起来问题不是很大。然后由于第一次用英文聊天还开始巨社牛地问他 US 的情况还 “让我们说中文 jusk joking :D” 捂脸捂脸。但题是真的不会做太牛魔,爆干净了。请务必诋毁我(鞠躬鞠躬流汗流汗变猫猫飞机耳阴暗地爬走)。 *** ### [CF914G Sum the Fibonacci](https://www.luogu.com.cn/problem/CF914G) 感觉这题应该放在上面 qwq。 观察三项,第一项是个子集卷积,第二项就是本身,第三项是自己卷自己的异或卷积,分别 FWT 做一下,然后乘上斐波那契的贡献,最后三个东西与卷积起来就是答案。基础好题啊,别被绕晕就很清晰。 现在我决定不要每次减法都保证运算结果为正,只要值域 $(-mod, mod)$ 就行,注意中间只能两两相加和最后转正即可。 ### [P4221 WC2018 州区划分](https://www.luogu.com.cn/problem/P4221) 哇,这就是半在线卷积! 先搓一个 $3^n$ dp,式子一写是 $f_s = (\frac{1}{\sum_{i\in s}w_i})^p \cdot \sum_{t \subsetneq s} f_t \cdot (\sum_{j\in s-t}w_j)^p \cdot fl_{s-t}$,显然就可以写成是 $f_s = c_s \cdot \sum_{t \subsetneq s} f_t \cdot g_{s-t}$,后面就是两玩意子集卷积起来,但是问题是其中一项是我们要求的答案本身,那我们就按照集合大小从小到大算答案,就像子集卷积一样做,只不过每次算完所有 $|s| = cnt$ 的答案之后,要 IFWT 出来把不符合 $|s| = cnt$ 的清零并全部乘上前面的系数。 中间运算不管正负是爽,但哥们别忘了最后转正! ### [ABC212H Nim Counting](https://www.luogu.com.cn/problem/AT_abc212_h) 经典 Nim 就是全异或,然后你其实要求一个多项式的等比数列和,FWT 之后对每一个点值等比数列求和即可。 怎么比上面的斐波那契还要基础 qwq。 ### [ARC100E Or Plus Max](https://www.luogu.com.cn/problem/AT_arc100_c) 智慧题。我们想求出 $\max \limits_{i | j \le k} a_i + a_j$,我们从小到大做每次取前缀最大,就转化成求 $\max \limits_{i | j = k} a_i + a_j$,但是这样还是不好做,注意到或运算有奇异性质,改成 $\max \limits_{i | j \in k} a_i + a_j$ 答案不劣,然后这个式子就是 $\max \limits_{i \in k, j \in k} a_i + a_j$,就随便高维前缀和做了。 ### [CF1033F Boolean Computer](https://www.luogu.com.cn/problem/CF1033F) **正虚像**把这题开到 $w = 16、n = 10^5、m = 10^5$ 之后搬进了 nfls 的传奇 **CSP** 模拟赛。~~CSP 传奇美妙至极~~ 原题 $w = 12,n = 3\times 10^4,m = 5\times10^4$,做法是首先六中位运算只与当前位集合有关顺序无关,本质只有 $(0,0),(1,0),(1,1)$ 三种也就是三进制下和为 $0、1、2$ 三种,那每个数转三进制后直接 $n^2$ 暴力枚举得到加起来值为 $val$ 有多少个有序对,然后给定一个运算 $s$ 后,六种位运算只有两种情况:合法情况个数 $1$(设为情况 A)、合法情况个数 $2$(设为情况 B)。直接对每一位分讨取的哪个合法的情况,设 $k$ 为情况 B 的位的个数,则复杂度就是 $O(2^k)$ 的。总复杂度 $O(n^2 + \sum \limits_{i = 1}^m 2^{k_i})$。 然后看加强版。首先 $n^2$ 计算 $val$ 的部分可以用三进制 FWT 不进位加法(其实直接加就行不会进位)优化为 $w3^w$,然后开始平衡复杂度:设阙值 $B$,当 $k \le B$ 时,跑上面的暴力枚举每一位集合,单次查询复杂度 $2^k$。 当 $k > B$ 时,我们思考上面做法的本质,是钦定所有位答案为 $0$,也就是枚举 B 的具体情况把 B 转成 A,现在 B 太多了我们考虑反过来把 A 转成 B。对于这一位设 $f_s$ 表示这一位三进制合法集合为 $s$ 的方案数,B 转成 A 相当于 $f_{\{0, 1\}} = f_{\{0\}} +f_{\{1\}}$;而 A 转成 B 考虑容斥,若只有 $\{0\}$ 合法那我们要求的答案就是 $f_{\{0\}}=\frac{f_{\{0, 1\}} +f_{\{0,2\}} - f_{\{1,2\}}}{2}$,其他同理,这样就把所有 A 情况用 $3^{w-k}$ 全转为 B。现在要求的就是给一个三进制数 $X$,求有多少对 $a_i + a_j$ 的**每一位**都与 $X$ **不同**。这个考虑预处理,然后继续魔改三进制 FWT,直接在刚才不进位加法做完后,对每一位做 $f'_0 = f_1+f_2,f'_1 = f_0+f_2,f'_2 = f_0+f_1$ 的变换即可。 我天,最后取 $B = 10$,这题复杂度就变成了 $O(w3^w + \sum \limits_{i = 1}^m \min(2^{k_i}, 3 ^{w - k_i}))$。我天直接无敌了啊。[CF 代码](https://codeforces.com/contest/1033/submission/296789558)(其中单位根其实是 $-\frac{1}{2} + \frac{\sqrt{3}}{2} \cdot i$,注释写成 $\frac{\sqrt{2}}{2}$ 是写错了 qwq),CF 空间太小了只能少开点数组,实际做 $w = 16$ 是要开 $3^w$ 的数组的。 ### [P5406 THUPC2019 找树](https://www.luogu.com.cn/problem/P5406) zak 讲题。~~浅谈随机化~~。神仙题。 首先注意到这题不是最优化题而是计数题,我们算出运算权值为 $V$ 的生成树个数,如果最大的不为零就是答案,考虑矩阵树定理,算的其实是 $\sum \limits_T \prod \limits_{e \in T} w_{T,e}$,那我们将权值写成集合幂级数的形式,那乘法就是卷积,加法直接加,按位做不同的 FWT 后就变成了点乘和点加,然后发现我们并不会做值是集合幂级数的高斯消元,那回到行列式定义,把式子感受一下发现其实对于集合幂级数的每一项系数独立,那直接把每一项拆出一个矩阵做 $2^w$ 次高斯消元(因为全是线性变换所以没问题),然后得到行列式的集合幂级数,再 IFWT 回去即可。实际数量可能非常大,直接模一个大质数即可,最后生成树数量是这个质数的倍数几乎不可能发生(这是这题跟随机化唯一有关的部分)。复杂度 $O(2^wn^3)$。 ### [JOISC 2023-final D Two Xor](https://atcoder.jp/contests/jsc2023-final/tasks/jsc2023_final_d?lang=en) 联考 11.21 T1,还加强到了 $n = 10^6,a_i \le 2^{22}$,无敌了。 首先异或两次等价于把所有数分为四个集合分别异或 $0,x,y,x \oplus y$。然后你二分答案可以把 $0$ 集合去掉,然后再在 01trie 上大力分讨,【一万种】讨论后最困难的情况会出现形如三棵子树,分别异或 $x,y,x \oplus y$,要使最大值最小。子树内异或一个值的最小最大值可以每次直接 dfs 处理,然后对 $x、y$ 两棵树进行 01 FWT 异或卷积就可以判断,特别地我们同样可以摸一个大质数防止答案爆 int,同时我们只用 FWT 还没有做到的后面的位即可,不用每次都做满 $2^{18}$,而且由于分讨,至少会少掉两层。这是 $\log^2$ 的,但你改成按位确定,我们每次只要跑最高 $l$ 位的 FWT,这样分析就变成单 $\log$ 了!太神了这题。 给出我的大分讨 $\log^2$ 代码:[让我们分类讨论](https://atcoder.jp/contests/jsc2023-final/submissions/60850636)! *** 其中一些题来自上述的两个博客,但去掉了牛客多校(把我,艹死了)、多项式 ln exp(主要在 cmd 的【集合幂级数】)、以及要用到分圆多项式的题~~当然是因为我不会这些科技~~,加上了之前训练见到的 FWT 题。多项式高科技太厉害,下次别出现在我的排位里,小清新 FWT 多可爱,国服密码机会自己开。