FWT

快速沃尔什变换

P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

或卷积

\[C_i = \sum_{ j | k = i } A_j \times B_k \]

思路就是把卷积转化为 $ O(n) $ 点积,类似于FFT。

考虑这样一个性质: $ i|j = i , i|k = i \Rightarrow i | (j|k) = i $ 。

令 $ FWT[A_i] = \sum_{i|j = i} A_j $ ,可以得到:

\[ \begin{aligned} FWT[A_i] \times FWT[B_i] & = \sum_{i|j = i}A_j \times \sum_{i|k = i}B_k \\ & = \sum_{ i | (j|k) = i } A_j \times B_k \\ & = FWT[C_i] \end{aligned} \]

现在只要快速实现变换和逆变换,就能完美解决问题。

按照FFT分治思路,每次根据当前位的值是0是1分成两个子问题:$ A = A_0 + A_1 $

显然有 $ FWT[A] = merge( FWT[A_0] , FWT[A_0] + FWT[A_1] ) $

其中 merge 表示将两个多项式拼接起来, + 表示两个多项式按位相加。

CODE
void OR(ll *x,int len){
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;k++)
				x[j+k+i]=(x[j+k+i]+x[j+k])%mod;
}
void IOR(ll *x,int len){
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;k++)
				x[j+k+i]=(x[j+k+i]-x[j+k]+mod)%mod;
}

与卷积

几乎没什么区别。

\[FWT[A_i] = \sum_{i\&j = i} A_j \]

\[FWT[A] = merge( FWT[A_0] + FWT[A_1] , FWT[A_0] ) \]

CODE
void AND(ll *x,int len){
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;k++)
				x[j+k]=(x[j+k]+x[j+k+i])%mod;
}
void IAND(ll *x,int len){
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;k++)
				x[j+k]=(x[j+k]-x[j+k+i]+mod)%mod;
}

异或卷积

定义 $ x \oplus y $ 代表 \(x\)\(y\) 按位异或, $ x \otimes y $ 代表 $ \text{popcount}( x \oplus y ) \bmod 2 $

\[C_i = \sum_{j \oplus k = i} A_j \times B_k \]

观察性质: $ i \otimes ( j \oplus k ) = ( i \otimes j ) \oplus ( i \otimes k ) $

\[FWT[A_i] = \sum_{i \otimes j = 0} A_j - \sum_{i \otimes j = 1} A_j \]

\[\begin{aligned} FWT[A_i] \times FWT[B_i] &= (\sum_{i \otimes j = 0} A_j - \sum_{i \otimes j = 1} A_j) \times (\sum_{i \otimes k = 0} B_k - \sum_{i \otimes k = 1} B_k) \\ &= \sum_{i \otimes (j \oplus k) = 0} A_j \times B_k - \sum_{i \otimes (j \oplus k) = 1} A_j \times B_k \\ &= FWT[C_i] \end{aligned} \]

\[FWT[A] = merge( FWT[A_0] + FWT[A_1] , FWT[A_0] - FWT[A_1] ) \]

CODE
void XOR(ll *x,int len){
	for(int i=1;i<len;i<<=1){
		for(int j=0;j<len;j+=(i<<1)){
			for(int k=0;k<i;k++){
				x[j+k]=(x[j+k]+x[j+k+i])%mod;
				x[j+k+i]=(x[j+k]-x[j+k+i]*2+mod*2)%mod;
			}
		}
	}
}
void IXOR(ll *x,int len){
	for(int i=1;i<len;i<<=1){
		for(int j=0;j<len;j+=(i<<1)){
			for(int k=0;k<i;k++){
				ll fir=(x[j+k]+x[j+k+i])*ny2%mod;
				ll sec=(x[j+k]-x[j+k+i]+mod)*ny2%mod;
				x[j+k]=fir;
				x[j+k+i]=sec;
			}
		}
	}
}

例题

P3175 [HAOI2015] 按位或

题意:维护一个数字 \(x\) ,开始时是0,每次在 $ [0,2^n) $ 中随机一个数,第 \(i\) 个数的概率为 $ p_i $ ,进行操作 $ x \leftarrow x \oplus i $ ,问 $ x = 2^n -1 $ 的期望操作次数。 $ n \le 20 $

一个 $ O(3^n) $ 的暴力是显然的,不过没什么前途。

前置知识:min-max容斥

\[\max(S) = \sum_{ T \subseteq S , T \neq \varnothing } (-1)^{ |T| + 1 } \min(T) \]

\[\min(S) = \sum_{ T \subseteq S , T \neq \varnothing } (-1)^{ |T| + 1 } \max(T) \]

简单证明一下:

不妨先假设所有元素互不相同,相同的可以扰动一下,不影响结果。

对于一个元素 \(x\) ,它在 \(S\) 中排名为第 \(k\) 小,显然包含它的子集 \(T\) 有 $ 2^{n-k} $ 个,其中 $ 2^{n-k-1} $ 个子集大小是奇数,另外 $ 2^{n-k-1} $ 个是偶数,两者相消结果是0。一个例外是 $ k=n $ ,即 \(x\) 是最大值,只会统计一次。证毕。

以上两个式子可以直接套上期望。

\[E( \max(S) ) = \sum_{ T \subseteq S , T \neq \varnothing } (-1)^{ |T| + 1 } E( \min(T) ) \]

在本题中,设 \(S\) 中元素为每一位变成1的操作次数,$ \max(S) $ 就是 \(x\) 变成 $ 2^n - 1 $ 的操作次数。

注意到

\[E( \min(T) ) = \frac{1}{ \sum_{R \cap T \neq \varnothing , x \in R} p_x } \]

容斥一下再跑FWT即可。

复杂度 $ O(2^n n) $

P5387 [Cnoi2019] 人形演舞

打表或者直接发现:$ SG( 2^k + p ) = p+1 , p < 2^k $

上SG定理,当 $ \text{mex}_{i=1}^{n} { SG(x_i) } $ 不为0时,先手必胜。

快速幂套FWT。$ O( m (\log m + \log n) ) $

posted @ 2025-08-27 21:55  Abnormal123  阅读(36)  评论(3)    收藏  举报