FWT(快速沃尔什变换)

FWT(快速沃尔什变换)

前言

萌新刚学多项式 1ms,有误或者不严谨指出欢迎指出,感谢大佬!

参考

OI Wiki

FWT快速沃尔什变换学习笔记

题解 P4717 【【模板】快速沃尔什变换】

位运算卷积(FWT) & 集合幂级数

鸽掉的介绍,我是 OIer 诶,不是 MOer 啊,要这么多证明干什么!直接背代码好了!写多了自然就理解了啊!

简介

使用类似 FFT 的方法求位运算卷积。

我们熟知的多项式卷积是求 \(\sum_{i=1}^n a_i b_{n-i}\)

对于位运算 \(\oplus\),可以是 or,and,xor。其卷积大概是 \(\sum_{i=1}^n \sum_{i \oplus j = n} a_i b_j\)

比如或卷积是 \(\sum_{i=1}^n \sum_{i|j = n} a_i b_j\),也就是我们常说的枚举子集。

对于 \(c_n = \sum_{i=1}^n \sum_{i \oplus j = n} a_i b_j\),我们要求出 \(c_n\) 或者 \(\{c_i\}\),最朴素的暴力枚举 \(i,j\) 时间复杂度是 \(O(n^2)\) 的。考虑使用 FWT 优化到 \(O(n \log n)\)

算法流程

记对序列 \(A=\{a_i\}\) 进行 FWT 变换后的序列(形式幂级数)是 \(fwt(A)\)

\(A,B,C\) 均指序列,\(a_i\) 之类是序列的第 \(i\) 项(从 \(0\) 开始记下标)。\(fwt(A)\) 之类是序列,\(fwt(A)_i\) 之类是指序列的第 \(i\) 项(同样从 \(0\) 开始记下标)。

我们要构造 \(A \oplus B = C \Longleftrightarrow fwt(A) \cdot fwt(B) = fwt(C)\)。需要再 \(O(n \log n)\) 的时间复杂度内计算 \(A \to fwt(A)\)\(fwt(A)\to A\),和线性求出 \(fwt(A) \cdot fwt(B) = fwt(C)\)

其中 \(A \oplus B = C\)\(c_n = \sum_{i \oplus j = i} a_i b_j\),即位运算卷积。

\(fwt(A) \cdot fwt(B) = fwt(C)\)\(fwt(C)_n = fwt(A)_n fwt(B)_n\),即点值相乘。

至于为什么要这么定义我还不知道,但是这么定义是可以做的

\([i,j]\) 表示 \(a_j\)\(fwt(A)_i\) 的贡献系数。应该有 \([i,j] \in \{0,1\}\)

\(fwt(A)_i=\sum_{j=0}^i [i,j] a_j\)


我们需要知道 \([i,j]\) 是什么,即如何构造 \(fwt(A)\)

\[\begin{aligned} fwt(A)_i \cdot fwt(B)_i & = fwt(C)_i\\ \sum_{j=0}^i [i,j] a_j \sum_{k=0}^i [i,k] b_k & = \sum_{p=0}^i [i,p] c_p\\ \sum_{j=0}^i \sum_{k=0}^i [i,j] [i,k] a_j b_k & = \sum_{p=0}^i [i,p] c_p\\ \end{aligned} \]

由于 \(A \oplus B = C\),我们有 \(c_n = \sum_{i \oplus j = i} a_i b_j\)

因此我们只需要保证对于所有的 \([i,p]=1\) 当且仅当 \(j \oplus k = p\) 的时候 \([i,j]=[i,k]=1\)

因为是位运算,我们只需要分别考虑每一位就好。即考虑 \([0,0],[0,1],[1,0],[1,1]\) 的取值。每种位运算都是不同的取值。


\(fwt(A)_i=\sum_{j=0}^i [i,j] a_j\),快速根据 \(A\)\(fwt(A)\)

code

不能理解,不是很好背诵。又学不会,又背不会,真是文理兼废,怎么办呢?

函数:

void calc() { rep(i,0,n-1) _mul(a[i],b[i]); }
void OR(int *f,int x=1) {
    for(int o=2,k=1; o<=n; o<<=1,k<<=1)     
        for(int i=0; i<n; i+=o)
            rep(j,0,k-1) _add(f[i+j+k],mul(f[i+j],x));
}
void AND(int *f,int x=1) {
    for(int o=2,k=1; o<=n; o<<=1,k<<=1)     
        for(int i=0; i<n; i+=o)
            rep(j,0,k-1) _add(f[i+j],mul(f[i+j+k],x));
}
void XOR(int *f,int x=1) {
    for(int o=2,k=1; o<=n; o<<=1,k<<=1)     
        for(int i=0; i<n; i+=o)
            rep(j,0,k-1) 
                _add(f[i+j],f[i+j+k]), f[i+j+k]=add(f[i+j],mod-add(f[i+j+k],f[i+j+k])), _mul(f[i+j],x), _mul(f[i+j+k],x);
}

调用方式:

OR(a), OR(b), calc(), OR(a,mod-1);
AND(a), AND(b), calc(), AND(a,mod-1);
XOR(a), XOR(b), calc(), XOR(a,ksm(2));
posted @ 2025-01-03 20:35  liyixin  阅读(1)  评论(0编辑  收藏  举报