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

最近在做多校联赛的题目,发现有一道题需要用到FWT,于是我就去学了一下。膜拜一下大神,本篇博客仅对这篇博客进行一些细节上的补充。

FWT要解决的问题是

Ck=ij=kaibi

其中 表示位运算and or xor 的其中一个

如果直接暴力枚举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(C)=tf(A)tf(B)

其中上面的*表示两个向量对应为相乘。

前人已经试出来了 tf 是一个怎么样的函数,对于xor的运算来说
当k=0是, tf(A)=tf(A)
当k>0时, tf(A)=(tf(A0)+tf(A1),tf(A0)tf(A1))

其中 A0 表示 A0..2k11 (就是向量A的前 2k1 维), A1 表示 A0..2k11 (就是向量A的后 2k1 维), 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=AB=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 长度为 2k1
所以 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)=(A0B0+A1B1,A0B1+A1B0)
这里表示着当 C0=A0B0+A1B1 表示着当A的最高位为0且B的最高位是0时,或者当A的最高位为1且B的最高位是1时,xor出来的结果最高位是0,当 C1=A0B1+A1B0 表示着当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+A1B0))
=(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),(A0A12)
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);  
}  

你可能感兴趣的:(ACM)