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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/john123741/article/details/76576925

最近在做多校联赛的题目,发现有一道题需要用到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+B0A1+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;d<n;d<<=1)  
        for(int m=d<<1,i=0;i<n;i+=m)  
            for(int j=0;j<d;j++)  
            {  
                int 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;d<n;d<<=1)  
        for(int m=d<<1,i=0;i<n;i+=m)  
            for(int j=0;j<d;j++)  
            {  
                int 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;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;  
    UFWT(a,n);  
}  
展开阅读全文

没有更多推荐了,返回首页