( ext {FWT}) 学习笔记
正常项的( ext {FWT})
在( ext {OI})中,我们经常会碰到这种问题:
- 给出一个长度为(n)的序列(a_{1,2,...,n},b_{1,2,...,n}),求出
其中(oplus)是定义的一种二进制下的运算。
对于这种问题,我们有一种通用的方法,我们称之为( ext {FWT})。
我们考虑对于一个(A)构造一个(FWT)变换序列,满足:
其中( imes)就是上文定义的卷积,(star)是按位乘法。
我们考虑定义一种二进制运算的函数(c(i,j)),满足:
于是,我们可以得到:
若存在:
则有:
而我们根据(FWT)的定义我们又可以得到:
于是,我们就可以得到:
不过因为是在二进制下的运算,所以一般构造的话都会满足
若
则满足:
于是,我们只需要知道(c(0/1,0/1))即可。
但是,我们现在仅仅可以在(Theta(n^2))的时间复杂度内求出和转换(FWT[A]),显然不能满足我们的对优秀的时间的渴求。
我们想一下在( ext {FFT})中,我们是如何做到(Theta(nlog n))转换的?分治!!!我们在( ext {FWT})中也可以用类似的方法。
我们考虑对于当前的(FWT[A]_i)应该如何求出。
可以得到:
其中(FWT[A_0/A_1])就是子集的一个变换,与( ext {FFT})类似。
我们发现如果我们构造转移矩阵:
其实(A o FWT[A])每一次变换就是乘上( ext {mat}),那么(FWT[A] o A)就是乘上( ext {mat})的逆矩阵。逆矩阵直接手动构造即可。
一些例子
(wedge)
对于并卷积,我们可以构造(c(i,j)=[j|i]),其中([j|i])表示的是二进制下的(j)是二进制下的(i)的子集(每一位(0/1)相当于该元素是否在当前集合出现)。
(vee)
对于或卷积,我们可以构造(c(i,j)=[i|j])。
(oplus)
对于异或卷积,我们可以构造(c(i,j)=(-1)^{|iwedge j|})。
模板题
就是上面三种运算的总和,代码戳这里打开。
非模板的一些例子
( ext {FST})
我们需要解决这样一个问题:
- 给出一个长度为(n)的序列(a_{1,2,...,n},b_{1,2,...,n}),求出:
对于这个问题,如果没有(jwedge k=0)的话,这就是一个板的( ext {FWT}) (vee)运算。我们发现其实(jwedge k=0)的条件就相当于(|j|+|k|=|jvee k|),于是,我们可以设二维数组(f_i),我们可以设转移式:
其中(h_{i,j}=[|j|=i]a_j,w_{i,j}=[|j|=i]b_j)。
很显然,最后的(c_i=f_{|i|,i})。
于是,我们就可以在(Theta(nlog^ 2 n))的时间复杂度内解决这个问题。
(k)进制下的( ext {FWT})
我们发现上面的这个东西其实都是在(2)进制下面计算的,那么如果我们要拓展到(k)进制我们应该怎么办呢?
很显然,我们应该定义广义的(wedge,vee,oplus)。
- $wedge $
在(k)进制下,定义(awedge b=min{a,b})
- (vee)
在(k)进制下,定义(avee b=max{a,b})
- (oplus)
在(k)进制下,定义(aoplus b=(a+b)mod k)
因为(wedge,vee)不是很常用,所以这里着重介绍一下(oplus)。
我们要考虑如何构造(c(i,j)),我们发现我们需要满足:
我们在脑中想一下,诶,似乎单位根满足这个条件诶!
于是,我们可以构造矩阵:
而它的逆矩阵就是: