【模板】快速沃尔什变换 FWT
FWT 重要性质:处理哪一位(枚举 \(len,k\)) 的顺序无关紧要,因为每一层的处理与长度无关。
很重要!
problem
解决形如
\[c_k=\sum_{i\oplus j=k}a_ib_j.
\]
的卷积式子,这里关注 \(\oplus=\{\text{or},\text{and},\text{xor}\}\)。
solution-xor
令 \(c=a\otimes_{\text{xor}} b\),表示 \(\text{xor}\) 卷积。显然有交换律,结合律,分配律。
将 \(a,b,c\) 按照二进制最高位奇偶分段为 \(a_0,a_1,b_0,b_1,c_0,c_1\)。
则有:
\[\begin{aligned}
c_0&=(a_0\otimes_{\text{xor}}b_0)+(a_1\otimes_{\text{xor}}b_1).\\
c_1&=(a_0\otimes_{\text{xor}}b_1)+(a_1\otimes_{\text{xor}}b_0).
\end{aligned}\]
观察到:
\[\begin{aligned}
(a_0+a_1)\otimes_{\text{xor}}(b_0+b_1)&=a_0\otimes_{\text{xor}}b_0+a_1\otimes_{\text{xor}}b_1+a_0\otimes_{\text{xor}}b_1+a_1\otimes_{\text{xor}}b_0.\\
(a_0-a_1)\otimes_{\text{xor}}(b_0-b_1)&=a_0\otimes_{\text{xor}}b_0+a_1\otimes_{\text{xor}}b_1-a_0\otimes_{\text{xor}}b_1-a_1\otimes_{\text{xor}}b_0.
\end{aligned}\]
记这两个为 \(d_0,d_1\),我们的思路是算出 \(d_0,d_1\),然后 \(c_0=\frac{1}{2}(d_0+d_1),c_1=\frac{1}{2}(d_0-d_1)\)。
所以:
\[\begin{matrix}
&a_0 &a_1 &b_0 &b_1\\
&\Downarrow &\Downarrow &\Downarrow &\Downarrow \\
&a_0+a_1 &a_0-a_1 &b_0+b_1 &b_0-b_1
\end{matrix}\]
相当于我们篡改了我们要卷的东西,然后计算答案的时候改回去变成对的。假设继续递归算了 \(d_0,d_1\),则回溯时
\[\begin{matrix}
&c_0 &c_1\\
&\Uparrow &\Uparrow \\
&\frac{1}{2}(d_0+d_1) &\frac{1}{2}(d_0-d_1)
\end{matrix}\]
嗯嗯做完了。实际代码实现会有点鬼畜,不过知道这些式子怎么来的就行了 QAQ
复杂度 \(O(n\log n)\) 呐。
点击查看代码
const int P=998244353;
void red(LL&x){x=(x%P+P)%P;}
void fwt_xor(LL *f,int n,int op){
for(int len=2,k=1;len<=n;len<<=1,k<<=1){
for(int i=0;i<n;i+=len){
for(int j=0;j<k;j++){
LL ta=f[i+j],tb=f[i+j+k];
red(f[i+j]=(ta+tb)*op);
red(f[i+j+k]=(ta-tb)*op);
}
}
}
}
fwt_xor(a,n,1),fwt_xor(b,n,1),multiple(a,b,c,n),fwt_xor(c,n,499122177);
//multiple 是逐位相乘,499122177 是 2 的逆元
solution-or
令 \(c=a\otimes_{\text{or}} b\),表示 \(\text{or}\) 卷积。显然有交换律,结合律,分配律。
我们复读一遍上面的过程:因为
\[\begin{aligned}
c_0&=a_0\otimes_{\text{or}}b_0.\\
c_1&=a_0\otimes_{\text{or}}b_1+a_1\otimes_{\text{or}}b_0+a_1\otimes_{\text{or}}b_1.
\end{aligned}\]
蝴蝶变换来啦~
\[\begin{aligned}
a_0\otimes_{\text{or}}b_0&=a_0\otimes_{\text{or}}b_0.\\
(a_0+a_1)\otimes_{\text{or}}(b_0+b_1)&=a_0\otimes_{\text{or}}b_0+a_1\otimes_{\text{or}}b_1+a_0\otimes_{\text{or}}b_1+a_1\otimes_{\text{or}}b_0.
\end{aligned}\]
\[\begin{matrix}
&a_0 &a_1 &b_0 &b_1\\
&\Downarrow &\Downarrow &\Downarrow &\Downarrow \\
&a_0 &a_0+a_1 &b_0 &b_0+b_1
\end{matrix}\]
\[\begin{matrix}
&c_0 &c_1\\
&\Uparrow &\Uparrow \\
&d_0 &d_1-d_0
\end{matrix}\]
点击查看代码
void fwt_or(LL *f,int n,int op){
for(int len=2,k=1;len<=n;len<<=1,k<<=1){
for(int i=0;i<n;i+=len){
for(int j=0;j<k;j++)
red(f[i+j+k]+=f[i+j]*op);
}
}
}
fwt_or(a,n,1),fwt_or(b,n,1),multiple(a,b,c,n),fwt_or(c,n,-1);
solution-and
这次是真的反过来直接上结论了 QAQ
\[\begin{matrix}
&a_0 &a_1 &b_0 &b_1\\
&\Downarrow &\Downarrow &\Downarrow &\Downarrow \\
&a_0+a_1 &a_1 &b_0+b_1 &b_1
\end{matrix}\]
\[\begin{matrix}
&c_0 &c_1\\
&\Uparrow &\Uparrow \\
&d_0-d_1 &d_1
\end{matrix}\]
代码都是反过来呢。
点击查看代码
void fwt_and(LL *f,int n,int op){
for(int len=2,k=1;len<=n;len<<=1,k<<=1)
for(int i=0;i<n;i+=len){
for(int j=0;j<k;j++){
red(f[i+j]+=f[i+j+k]*op);
}
}
}
fwt_and(a,n,1),fwt_and(b,n,1),multiple(a,b,c,n),fwt_and(c,n,-1);
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/template-fwt.html