To_Heart—总结——FWT(快速沃尔什变换)

闲话

这个比FFT简单了很多呢,,大概是我可以学懂的水平!

好像是叫 快速沃尔什变换 ?

拿来求什么

以 FFT 来类比。我们 FFT 可以在 O ( n l o g n ) \mathrm{O(nlogn)} O(nlogn) 的复杂度下实现求解:

C k = ∑ i + j = k A i × B j C_k=\sum_{i + j=k} A_i \times B_j Ck=i+j=kAi×Bj

那么 FWT 的作用就是再 O ( n l o g n ) \mathrm{O(nlogn)} O(nlogn) 的时间复杂度下面求解:

C k = ∑ i ⊕ j = k A i × B j C_k=\sum_{i \oplus j=k} A_i \times B_j Ck=ij=kAi×Bj

其中的 ⊕ \oplus 是位运算异或的意思。 FWT 应该是支持 ⊕ \oplus ∣ | & \& & 三种位运算的。

然后就准备开始讲这三种位运算分别对应的算法。

我们定义 F W T ( A ) i = ∑ j ∣ i = i A j \mathrm{FWT(A)_i}=\sum_{j|i=i} A_j FWT(A)i=ji=iAj。根据定义可以知道 F W T ( A ) \mathrm{FWT(A)} FWT(A) 是一个由 A A A 构造出来的多项式。先不管如何构造,我们考虑 F W T ( A ) \mathrm{FWT(A)} FWT(A) 有什么用。我们发现:

F W T ( A ) × F W T ( B ) \mathrm{FWT(A)} \times \mathrm{FWT(B)} FWT(A)×FWT(B)
= ∑ i = 0 F W T ( A ) i × F W T ( B ) i =\sum_{i=0} \mathrm{FWT(A)_i} \times \mathrm{FWT(B)_i} =i=0FWT(A)i×FWT(B)i
= ∑ i = 0 ( ∑ j ∣ i = i A j × ∑ k ∣ i = i B k ) =\sum_{i=0} ( \sum_{j|i=i} A_j \times \sum_{k|i=i} B_ k) =i=0(ji=iAj×ki=iBk)

= ∑ i = 0 ∑ ( j ∣ k ) ∣ i = i A j × B k =\sum_{i=0} \sum_{(j|k)|i=i} A_j \times B_ k =i=0(jk)i=iAj×Bk

= ∑ i = 0 ∑ ( j ∣ k ) ∣ i = i C i =\sum_{i=0} \sum_{(j|k)|i=i} C_i =i=0(jk)i=iCi
= F W T ( C ) =\mathrm{FWT(C)} =FWT(C)

所以我们发现可以在 O ( n ) \mathrm{O(n)} O(n) 的时间复杂度下实现由 A , B A,B A,B C C C 的转换。所以现在的问题就是如何在 O ( n l o g n ) \mathrm{O(nlogn)} O(nlogn) 及以下的时间复杂度中求出 F W T ( A ) \mathrm{FWT(A)} FWT(A)

考虑分治。假设 A A A 2 n 2^n 2n 项,那么 A 0 A_0 A0 表示 A A A 的前 2 n − 1 2^{n-1} 2n1 A 1 A_1 A1 表示 A A A 的后 2 n − 1 2^{n-1} 2n1 项。

然后就可以得到一个崭新的转移:

F W T ( A ) = { F W T ( A 0 ) , F W T ( A 1 ) + F W T ( A 0 ) n ≥ 1 A n = 0 \mathrm{FWT(A)}=\begin{cases}{\mathrm{FWT(A_0)},\mathrm{FWT(A_1)}+\mathrm{FWT(A_0)}}&{n\geq 1}\\{A}&{n=0}\end{cases} FWT(A)={FWT(A0),FWT(A1)+FWT(A0)An1n=0

这个 ,可以理解成把两个多项式拼起来。

如何理解这个式子呢? n = 0 n=0 n=0 的边界很好理解,问题在上面一个式子。

你考虑 A 0 A_0 A0 A 1 A_1 A1 的区别:在 A A A 中且在 A 0 A_0 A0中 的项的下标的最高位一定是 0 ;在 A A A 中且在 A 1 A_1 A1中 的项的下标的最高位一定是 1 ;

所以你发现从 A 0 A_0 A0 A 1 A_1 A1 A A A 中只有可能是 A 0 A_0 A0 所在的项给 A 1 A_1 A1 所在的项做贡献。

所以就可以做到 O ( n l o g n ) \mathrm{O(nlogn)} O(nlogn) 的时间复杂度实现从 A A A F W T ( A ) \mathrm{FWT(A)} FWT(A) 的转换了。

但是还需要实现从 F W T ( A ) \mathrm{FWT(A)} FWT(A) A A A 的转换,也就是逆转换。

你感性理解一下,大概就是 A_0 会影响的两个位置,其中一个只有 A_0 ,另一个是 A_0+A_1 ,所以设 x_0 为改变的第一个位置, x_1 为改变的第二个位置,那么有 x = A 0 x=A_0 x=A0 y = A 0 + A 1 y=A_0+A_1 y=A0+A1 。现在是知道了 x 和 y ,所以 A 0 = x A_0=x A0=x A 1 = y − A 0 = y − x A_1=y-A_0=y-x A1=yA0=yx

那么关于 或运算 的 FWT 就可以实现了:

void OR(ll *a,int n,int op){ //op=1是顺转换,op=-1是逆转换
	for(int mid=1;mid<n;mid<<=1) for(int len=mid<<1,j=0;j<n;j+=len) for(int i=j;i<j+mid;i++)
		a[i+mid]=(a[i+mid]+a[i]*op+Mod)%Mod;
}

这个和 或 差不多,但是注意到只有 A 1 A_1 A1 可以向 A 0 A_0 A0 贡献,所以反过来。

void AND(ll *a,int n,int op){
	for(int mid=1;mid<n;mid<<=1) for(int len=mid<<1,j=0;j<n;j+=len) for(int i=j;i<j+mid;i++)
		a[i]=(a[i]+a[i+mid]*op+Mod)%Mod;
}

异或

这个可能要麻烦一点。

异或本身并不好统一的下标之间的关联。什么意思呢?对于 或 ,我们可以很清楚的发现对于所有情况都是 A 0 A_0 A0 A 1 A_1 A1 做贡献;对于 与,所有情况都是 A 1 A_1 A1 A 0 A_0 A0 做贡献。但是异或并不满足这样的性质。所以需要考虑一种构造 F W T \mathrm{FWT} FWT 的方式使得 A 0 A_0 A0 A 1 A_1 A1 的做贡献方式是一成不变的。

那么考虑怎么找到构造方式。

posted @ 2023-03-25 11:19  To_Heart  阅读(5)  评论(0编辑  收藏  举报  来源