FWT——快速沃尔什变换

来源:互联网 发布:mac怎么安装输入法 编辑:程序博客网 时间:2024/06/08 11:31

前言

这个东西就是方便做题···也没什么大用处,写起来倒是比FFT简单不少,原理也很简单。

问题描述

我们知道,在FFT中,我们快速解决了C=(AB)Ci=nj=0AjBij
我们现在需要快速解决一类位运算卷积问题。即,Ci=jk=iAjBk。这个⊕可以指xor,and,or等等等等。

FWT

我们记C=AB的意义为Ci=AiBiC=A±B的意义为Ci=Ai±Bi
为了快速解决C=AB,以前的贤者搞出了一种变换,记为tf(transfer),这种变换满足tf(C)=tf(A)tf(B)。哦,这样一来,我们先搞出A和B的变换,再乘起来,再使用utf,逆变换,把tf(C)变回C(像FFT中逆DFT一样)。便实现了快速卷积。

transfer

就以异或举例。和FFT一样的,为了快速算,必须补齐到2的幂次的大小。
A=a0,a1,a2....a2k
tf(A)=(tf(A0)+tf(A1),tf(A0)tf(A1))
其中A0=<a0,a1,...,a2k11>,A1=<a2k1,a2k1+1,...,a2k1>
即把A中的下标按照二进制最高位为0或1分成前后两部分(前面的为A0,后面的为A1),分治下去做。
分治之后得到tf(A0)tf(A1)。然后tf(A)的前半部分(即[0,2k11])为tf(A0)+tf(A1),后半部分(即[2k1,2k1])为tf(A0)tf(A1)
其实这个过程就相当于是一个精妙的构造,使得原问题转化了。
至于这样为什么是对的,证明起来很麻烦,思路大概是:我们随便搞个A,B,C,然后按照原意义给出CiAj,Bk的关系,然后使用归纳法。即先证明两个元素的时候是可以的,然后再证明如果2k个可以,2k+1也可以。
这样就弄完了transfer

untransfer

得到了tf(C),我们再把它逆转换回来就好了。证明方法是一样的。

代码

十分简短,由于我只是去简单应用它,大概知道步骤,背下代码就行了,实现过程应该跟FFT有异曲同工之妙(划掉O(∩_∩)O
(我自己还没打,临时搬个别人的代码)

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;                  //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);  }  
原创粉丝点击