最近在做多校联赛的题目,发现有一道题需要用到FWT,于是我就去学了一下。膜拜一下大神,本篇博客仅对这篇博客进行一些细节上的补充。
FWT要解决的问题是
如果直接暴力枚举i和j,我们需要 O(n2) 的时间复杂度,但是FWT可以让这个问题在 O(nlogn) 的时间复杂度下解决这个问题。
它的思路有点类似于FFT,它们都是通过先做一个变换,然后直接相乘,最后逆变换得到的,而变换和逆变换的过程都是用折半、分治来完成。
以做xor的过程为例
这里用 @ 表示卷积运算, + 表示数字的加法运算, ∗ 表示数字的乘法运算。
设A,B为一个 2k 的向量,如果A,B不足2^k,可以用0去补全,
设C=A @ B,C也是一个 2k 的向量。
设 tf(A) 是对A做一次FWT的结果,结果也是一个2^k的向量,如果可以做FWT,需要满足的条件是:
前人已经试出来了 tf 是一个怎么样的函数,对于xor的运算来说
当k=0是, tf(A)=tf(A)
当k>0时, tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))
其中 A0 表示 A0..2k−1−1 (就是向量A的前 2k−1 维), A1 表示 A0..2k−1−1 (就是向量A的后 2k−1 维), tf(A0)+tf(A1) 和 tf(A0)−tf(A1) 是一个{2}^{k-1}的向量,而 (tf(A0)+tf(A1),tf(A0)−tf(A1)) 就表示把这两个相连连起来,成为一个{2}^{k}的向量。这里就是一个递归分治的过程。
下面我们就来证明当tf是上面的那个定义的时候,满足 tf(C)=tf(A)∗tf(B)
首先我们需要先证明一个引理 tf(A+B)=tf(A)+tf(B)
当k=0时 tf(A+B)=A+B=tf(A)+tf(B)
当k>0时
tf(A+B)
=(tf(A0+B0)+tf(A1+B1),tf(A0+B0)−tf(A1+B1))
其中 A0+B0 和 A1+B1 是长度为{2}^{k-1},所以
原式
=(tf(A0)+tf(B0)+tf(A1)+tf(B1),tf(A0)+tf(B0)−tf(A1)−tf(B1))
=(tf(A0)+tf(A1),tf(A0)−tf(A1))+(tf(B0)+tf(B1),tf(B0)−tf(B1))
=tf(A)+tf(B)
接下来我们还要证明 tf(C)=tf(A)∗tf(B)
当k=0时, tf(C)=C=A∗B=tf(A)∗tf(B)
当k>0时,
tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))
tf(B)=(tf(B0)+tf(B1),tf(B0)−tf(B1))
tf(A)∗tf(B)
=((tf(A0)+tf(A1))∗(tf(B0)+tf(B1)),(tf(A0)−tf(A1))∗(tf(B0)−tf(B1))
暴力拆开括号
=(tf(A0)∗tf(B0)+tf(A0)∗tf(B1)+tf(A1)∗tf(B0)+tf(A1)∗tf(B1),tf(A0)∗tf(B0)−tf(A0)∗tf(B1)−tf(A1)∗tf(B0)+tf(A1)∗tf(B1))
因为 tf(A0)∗tf(B0 长度为 2k−1 ,
所以 tf(A0)∗tf(B0=tf(A0@B0)
所以原式
=(tf(A0@B0)+tf(A1@B1)+tf(A0@B1)+tf(A1@B0),tf(A0@B0)+tf(A1@B1)−tf(A0@B1)−tf(A1@B0))
因为异或每个位都是独立的,而我们根据最高位是0还是1,把A和B都拆成了两部分。
所以 C=(C0,C1)=(A0∗B0+A1∗B1,A0∗B1+A1∗B0)
这里表示着当 C0=A0∗B0+A1∗B1 表示着当A的最高位为0且B的最高位是0时,或者当A的最高位为1且B的最高位是1时,xor出来的结果最高位是0,当 C1=A0∗B1+A1∗B0 表示着当A的最高位为0且B的最高位是1时,或者当A的最高位为1且B的最高位是0时,xor出来的结果最高位是1
所以
tf(C)=tf(C0,C1)
=tf(A0@B0+A1@B1,A0@B1+A1@B0)
=(tf(A0@B0+A1@B1)+tf(A0@B1+A1@B0),tf(A0@B0+A1@B1)−tf(A0@B1+A1∗B0))
=(tf(A0@B0)+tfA1@B1)+tf(A0@B1)+tf(A1@B0),tf(A0@B0)+tf(A1@B1)−tf(A0@B1)−tf(A1@B0))
=(tf(A0)∗tf(B0)+tf(A0)∗tf(B1)+tf(A1)∗tf(B0)+tf(A1)∗tf(B1),tf(A0)∗tf(B0)−tf(A0)∗tf(B1)−tf(A1)∗tf(B0)+tf(A1)∗tf(B1))
=((tf(A0)+tf(A1))∗(tf(B0)+tf(B1)),(tf(A0)−tf(A1))∗(tf(B0)−tf(B1))
=tf(C)
tf 函数的你函数 ntf 也是类似。
下面给出三种运算的 tf 函数和 ntf 函数:
xor
tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))
utf(A)=(utf(A0+A12),(A0−A12)
and
tf(A)=(tf(A0)+tf(A1),tf(A1))
utf(A)=(utf(A0)−utf(A1),utf(A1))
or
tf(A)=(tf(A0),tf(A1)+tf(A0))
utf(A)=(utf(A0),utf(A1)−utf(A0))
下面是代码
void FWT(int a[],int n)
{
for(int d=1;d1)
for(int m=d<<1,i=0;ifor(int j=0;jint x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
//xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;
//and:a[i+j]=x+y;
//or:a[i+j+d]=x+y;
}
}
void UFWT(int a[],int n)
{
for(int d=1;d1)
for(int m=d<<1,i=0;ifor(int j=0;jint x=a[i+j],y=a[i+j+d];
a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
//rev表示2在模mod下的逆元
//xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
//and:a[i+j]=x-y;
//or:a[i+j+d]=y-x;
}
}
void solve(int a[],int b[],int n)
{
FWT(a,n);
FWT(b,n);
for(int i=0;i1LL*a[i]*b[i]%mod;
UFWT(a,n);
}