FWT 底层逻辑

写在前面:

我在 这里 学的,大佬写得很用心,FWT的推导过程都是出自那里。

构造过程

F W T ( A ) [ i ] = ∑ j = 0 n − 1 c ( i , j ) A j FWT(A)[i]=\sum_{j=0}^{n-1}c(i,j)A_j FWT(A)[i]=j=0n1c(i,j)Aj

根据 F W T ( A ) ⋅ F W T ( B ) = F W T ( C ) FWT(A)\cdot FWT(B)=FWT(C) FWT(A)FWT(B)=FWT(C)
F W T ( A ) [ i ] F W T ( B ) [ i ] = F W T ( C ) [ i ] ∑ j = 0 n − 1 c ( i , j ) A [ j ] ∑ k = 0 n − 1 c ( i , k ) B [ k ] = ∑ p = 0 n − 1 c ( i , p ) C [ p ] ∑ j = 0 n − 1 ∑ k = 0 n − 1 c ( i , j ) c ( i , k ) A [ j ] B [ k ] = ∑ p = 0 n − 1 c ( i , p ) C [ p ] FWT(A)[i]FWT(B)[i]=FWT(C)[i]\\ \sum_{j=0}^{n-1}c(i,j)A[j]\sum_{k=0}^{n-1}c(i,k)B[k]=\sum_{p=0}^{n-1}c(i,p)C[p]\\ \sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A[j]B[k]=\sum_{p=0}^{n-1}c(i,p)C[p] FWT(A)[i]FWT(B)[i]=FWT(C)[i]j=0n1c(i,j)A[j]k=0n1c(i,k)B[k]=p=0n1c(i,p)C[p]j=0n1k=0n1c(i,j)c(i,k)A[j]B[k]=p=0n1c(i,p)C[p]
根据 A ∗ B = C A*B=C AB=C
c [ p ] = ∑ t 1 ⊕ t 2 A [ t 1 ] B [ t 2 ] ∑ p = 0 n − 1 c ( i , p ) C [ P ] = ∑ p = 0 n − 1 c ( i , p ) ∑ t 1 ⊕ t 2 = p A [ t 1 ] B [ t 2 ] ∑ j = 0 n − 1 ∑ k = 0 n − 1 c ( i , j ) c ( i , k ) A [ i ] B [ k ] = ∑ p = 0 n − 1 ∑ t 1 ⊕ t 2 = p c ( i , t 1 ⊕ t 2 ) A [ t 1 ] B [ t 2 ] ∑ j = 0 n − 1 ∑ k = 0 n − 1 c ( i , j ) c ( i , k ) A [ i ] B [ k ] = ∑ t 1 = 0 n − 1 ∑ t 2 = 0 n − 1 c ( i , t 1 ⊕ t 2 ) A [ t 1 ] B [ t 2 ] c[p]=\sum_{t_1\oplus t_2}A[t_1]B[t_2] \\ \sum_{p=0}^{n-1}c(i,p)C[P]=\sum_{p=0}^{n-1}c(i,p)\sum_{t_1\oplus t_2=p}A[t_1]B[t_2] \\ \sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A[i]B[k]=\sum_{p=0}^{n-1}\sum_{t_1\oplus t_2=p}c(i,t_1\oplus t_2)A[t_1]B[t_2]\\ \sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A[i]B[k]=\sum_{t_1=0}^{n-1}\sum_{t_2=0}^{n-1}c(i,t_1\oplus t_2)A[t_1]B[t_2] c[p]=t1t2A[t1]B[t2]p=0n1c(i,p)C[P]=p=0n1c(i,p)t1t2=pA[t1]B[t2]j=0n1k=0n1c(i,j)c(i,k)A[i]B[k]=p=0n1t1t2=pc(i,t1t2)A[t1]B[t2]j=0n1k=0n1c(i,j)c(i,k)A[i]B[k]=t1=0n1t2=0n1c(i,t1t2)A[t1]B[t2]
所以 c c c 只需要满足: c ( i , j ) c ( i , k ) = c ( i , j ⊕ k ) c(i,j)c(i,k)=c(i,j\oplus k) c(i,j)c(i,k)=c(i,jk)

则有 c ( i , j ) = c ( i 0 , j 0 ) c ( i 1 , j i ) c ( i 2 , j 2 ) . . . c(i,j)=c(i_0,j_0)c(i_1,j_i)c(i_2,j_2)... c(i,j)=c(i0,j0)c(i1,ji)c(i2,j2)... ,表示把二进制每一位的变化系数乘起来。(为什么?

因为这样的话对于每一个 t t t ,都有 c ( i t , j t ) c ( i t , k t ) = c ( i t , j t ⊕ k t ) → c ( i , j ) c ( i , k ) = c ( i , j ⊕ k ) c(i_t,j_t)c(i_t,k_t)=c(i_t,j_t\oplus k_t)\rightarrow c(i,j)c(i,k)=c(i,j\oplus k) c(it,jt)c(it,kt)=c(it,jtkt)c(i,j)c(i,k)=c(i,jk)


FWT快速求解

F W T ( A ) [ i ] = ∑ j = 0 n − 1 c ( i , j ) A [ j ] = ∑ j = 0 ( n / 2 ) − 1 c ( i , j ) A [ j ] + ∑ j = ( n / 2 ) n − 1 c ( i , j ) A [ i ] = ∑ j = 0 ( n / 2 ) − 1 c ( i 0 , j 0 ) c ( i ′ , j ′ ) A [ j ] + ∑ j = ( n / 2 ) n − 1 c ( i 0 , j 0 ) c ( i ′ , j ′ ) A [ i ] = c ( i 0 , 0 ) ∑ j = 0 ( n / 2 ) − 1 c ( i ′ , j ′ ) A [ j ] + c ( i 0 , 1 ) ∑ j = ( n / 2 ) n − 1 c ( i ′ , j ′ ) A [ i ] \begin{aligned} FWT(A)[i]&=\sum_{j=0}^{n-1}c(i,j)A[j]\\ &=\sum_{j=0}^{(n/2)-1}c(i,j)A[j]+\sum_{j=(n/2)}^{n-1}c(i,j)A[i]\\ &=\sum_{j=0}^{(n/2)-1}c(i_0,j_0)c(i',j')A[j]+\sum_{j=(n/2)}^{n-1}c(i_0,j_0)c(i',j')A[i]\\ &=c(i_0,0)\sum_{j=0}^{(n/2)-1}c(i',j')A[j]+c(i_0,1)\sum_{j=(n/2)}^{n-1}c(i',j')A[i] \end{aligned} FWT(A)[i]=j=0n1c(i,j)A[j]=j=0(n/2)1c(i,j)A[j]+j=(n/2)n1c(i,j)A[i]=j=0(n/2)1c(i0,j0)c(i,j)A[j]+j=(n/2)n1c(i0,j0)c(i,j)A[i]=c(i0,0)j=0(n/2)1c(i,j)A[j]+c(i0,1)j=(n/2)n1c(i,j)A[i]

i ′ i' i i i i 去掉首位剩下的数。

A 0 A_0 A0 为幂级数下标首位为 0 0 0 的部分,其余为 A 1 A_1 A1

那么:
F W T ( A ) [ i ] = c ( 0 , 0 ) F W T ( A 0 ) [ i ] + c ( 0 , 1 ) F W T ( A 1 ) [ i ]      ( i ∈ [ 0 , n / 2 ) ) F W T ( A ) [ i + ( n / 2 ) ] = c ( 1 , 0 ) F W T ( A 0 ) [ i ] + c ( 1 , 1 ) F W T ( A 1 ) [ i ]      ( i ∈ [ 0 , n / 2 ) ) FWT(A)[i]=c(0,0)FWT(A_0)[i]+c(0,1)FWT(A_1)[i]\ \ \ \ (i\in[0,n/2)) \\ FWT(A)[i+(n/2)]=c(1,0)FWT(A_0)[i]+c(1,1)FWT(A_1)[i]\ \ \ \ (i\in[0,n/2)) \\ FWT(A)[i]=c(0,0)FWT(A0)[i]+c(0,1)FWT(A1)[i]    (i[0,n/2))FWT(A)[i+(n/2)]=c(1,0)FWT(A0)[i]+c(1,1)FWT(A1)[i]    (i[0,n/2))
这里类似 F F T FFT FFT ,规模减半了。逆变换就是矩阵 c c c 变成其逆矩阵再做 F W T FWT FWT

对比FFT​ : F F T FFT FFT 分为两部分是以奇偶分的,而 F W T FWT FWT 是分为的前半部分和后半部分,所以不需要 F F T FFT FFT s w a p ( a [ i ] , a [ r e v [ i ] ] ) swap(a[i],a[rev[i]]) swap(a[i],a[rev[i]]) 这个操作。


位运算卷积

结论比推导简单,很好背,由于 c ( i , j ) c(i,j) c(i,j) 本身的性质,我们只需要知道 c ( [ 0 , 1 ] , [ 0 , 1 ] ) c([0,1],[0,1]) c([0,1],[0,1]) 就够了。

设矩阵 c = [ c ( 0 , 0 ) c ( 0 , 1 ) c ( 1 , 0 ) c ( 1 , 1 ) ] c=\begin{bmatrix} c(0,0)&c(0,1) \\ c(1,0)&c(1,1) \end{bmatrix} c=[c(0,0)c(1,0)c(0,1)c(1,1)]

  • Or 卷积
    c = [ 1 0 1 1 ]     1 c = [ 1 0 − 1 1 ] c=\begin{bmatrix} 1&0 \\ 1&1 \end{bmatrix}\ \ \ \frac{1}{c}=\begin{bmatrix} 1&0 \\ -1&1 \end{bmatrix} c=[1101]   c1=[1101]

  • And 卷积
    c = [ 1 1 0 1 ]     1 c = [ 1 − 1 0 1 ] c=\begin{bmatrix} 1&1 \\ 0&1 \end{bmatrix}\ \ \ \frac{1}{c}=\begin{bmatrix} 1&-1 \\ 0&1 \end{bmatrix} c=[1011]   c1=[1011]

  • Xor 卷积
    c = [ 1 1 1 − 1 ]     1 c = [ 1 / 2 1 / 2 1 / 2 − 1 / 2 ] c=\begin{bmatrix} 1&1 \\ 1&-1 \end{bmatrix}\ \ \ \frac{1}{c}=\begin{bmatrix} 1/2&1/2 \\ 1/2&-1/2 \end{bmatrix} c=[1111]   c1=[1/21/21/21/2]


FWT的性质

F W T ( A + B ) = F W T ( A ) + F W T ( B ) F W T ( c A ) = c F W T ( A ) X O R : c ( i , j ) = ( − 1 ) p o p c o u n t ( i & j ) a n d F W T   本 质 上 是 求 超 集 和 FWT(A+B)=FWT(A)+FWT(B)\\ FWT(cA)=cFWT(A)\\ XOR:c(i,j)=(-1)^{popcount(i\&j)} \\ andFWT\ 本质上是求超集和 FWT(A+B)=FWT(A)+FWT(B)FWT(cA)=cFWT(A)XOR:c(i,j)=(1)popcount(i&j)andFWT 


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

模板没什么好说的,(这是最好背最好理解的版本了2333

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vec;
const ll mod=998244353,inv2=499122177;
ll Cxor[2][2]={{1,1},{1,mod-1}},Ixor[2][2]={{inv2,inv2},{inv2,mod-inv2}},
Cor[2][2]={{1,0},{1,1}},Ior[2][2]={{1,0},{mod-1,1}},
Cand[2][2]={{1,1},{0,1}},Iand[2][2]={{1,mod-1},{0,1}};
void FWT(vec &F,ll c[2][2],int limit){
	while(F.size()<limit)F.push_back(0);
	for(int mid=1;mid<limit;mid<<=1)
		for(int j=0;j<limit;j+=(mid<<1))
			for(int k=0;k<mid;k++){
				ll x=F[j+k],y=F[j+k+mid];
				F[j+k]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod;
				F[j+k+mid]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod;
			}
}
vec f,g,a,b;
int main(){
//	freopen("test.in","r",stdin);
	ll n;
	cin>>n; n=1<<n;
	f.resize(n); g.resize(n);
	for(int i=0;i<n;i++) cin>>f[i];
	for(int i=0;i<n;i++) cin>>g[i];
    
    //Or
	a=vec(&f[0],&f[n]),b=vec(&g[0],&g[n]);
	FWT(a,Cor,n),FWT(b,Cor,n);
	for(int i=0;i<n;i++) a[i]=a[i]*b[i]%mod;
	FWT(a,Ior,n);
	for(int i=0;i<n;i++) cout<<a[i]<<' ';
	cout<<'\n';
    
    //And
	a=vec(&f[0],&f[n]),b=vec(&g[0],&g[n]);
	FWT(a,Cand,n),FWT(b,Cand,n);
	for(int i=0;i<n;i++) a[i]=a[i]*b[i]%mod;
	FWT(a,Iand,n);
	for(int i=0;i<n;i++) cout<<a[i]<<' ';
	cout<<'\n';
    
    //Xor
	a=vec(&f[0],&f[n]),b=vec(&g[0],&g[n]);
	FWT(a,Cxor,n),FWT(b,Cxor,n);
	for(int i=0;i<n;i++) a[i]=a[i]*b[i]%mod;
	FWT(a,Ixor,n);
	for(int i=0;i<n;i++) cout<<a[i]<<' ';
	cout<<'\n';
    
} 

AT4996 [AGC034F] RNG and XOR

一眼式
f x = 1 + ∑ i p i f x ⊕ i , f 0 = 0 f_x=1+\sum_ip_if_{x\oplus i},f_0=0 fx=1+ipifxi,f0=0
写成卷积形式
( p 0 , p 1 , . . . , p 2 N − 1 ) ⋅ ( f 0 , f 1 , . . . , f 2 N − 1 ) = ( y , f 1 − 1 , . . . , f 2 N − 1 ) (p_0,p_1,...,p_{2^N-1})\cdot(f_0,f_1,...,f_{2^N-1})=(y,f_1-1,...,f_{2^N-1}) (p0,p1,...,p2N1)(f0,f1,...,f2N1)=(y,f11,...,f2N1)
因为 ∑ i = 0 2 N − 1 p i × ∑ j = 0 2 N − 1 f j = ∑ i = 1 2 N − 1 ( f i − 1 ) + y \sum_{i=0}^{2^N-1} p_i\times\sum_{j=0}^{2^N-1} f_j=\sum_{i=1}^{2^N-1}(f_i-1)+y i=02N1pi×j=02N1fj=i=12N1(fi1)+y 所以 y = f 0 + 2 N − 1 y=f_0+2^N-1 y=f0+2N1

那么
( p 0 − 1 , p 1 , . . . , p 2 N − 1 ) ⋅ ( f 0 , f 1 , . . . , f 2 N − 1 ) = ( 2 N − 1 , − 1 , − 1 , . . . , − 1 ) (p_0-1,p_1,...,p_{2^N-1})\cdot(f_0,f_1,...,f_{2^N-1})=(2^N-1,-1,-1,...,-1) (p01,p1,...,p2N1)(f0,f1,...,f2N1)=(2N1,1,1,...,1)
进行 F W T FWT FWT,可以得到 P i ⋅ F i = X i P_i\cdot F_i=X_i PiFi=Xi,其中 P i , X i P_i,X_i Pi,Xi 都是常数。如果 P i , X i P_i,X_i Pi,Xi 0 0 0 就能直接求出 F i F_i Fi。考虑 X i X_i Xi 什么时候为 0 0 0 X i = ∑ j = 0 2 N − 1 ( − 1 ) ∣ i   a n d   j ∣ x i X_i=\sum_{j=0}^{2^N-1}(-1)^{|i\ and\ j|}x_i Xi=j=02N1(1)i and jxi,发现仅有 X 0 = 0 X_0=0 X0=0,因此还不能求出 F i F_i Fi 。但我们还知道 f 0 = ∑ i = 0 2 N − 1 F i 2 N = 0 f_0=\frac{\sum_{i=0}^{2^N-1}F_i}{2^N}=0 f0=2Ni=02N1Fi=0,因此 F i = − ∑ i = 1 2 N − 1 F i F_i=-\sum_{i=1}^{2^N-1}F_i Fi=i=12N1Fi 。最后一遍逆变换就能得到答案了。 O ( N 2 N ) O(N2^N) O(N2N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vec;
const ll mod=998244353,inv2=499122177;
ll Cxor[2][2]={{1,1},{1,mod-1}},Ixor[2][2]={{inv2,inv2},{inv2,mod-inv2}};
ll ksm(ll x,ll y){
	ll res=1;
	while(y){
		if(y&1) res=res*x%mod;
		x=x*x%mod; y>>=1;
	} 
	return res;
}
void FWT(vec &a,ll c[2][2],int limit){
	while(a.size()<limit) a.push_back(0);
	for(int mid=1;mid<limit;mid<<=1)
		for(int j=0;j<limit;j+=(mid<<1))
			for(int k=0;k<mid;k++){
				ll x=a[j+k],y=a[j+k+mid];
				a[j+k]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod;
				a[j+k+mid]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod;
			}
}
vec p,f,g;
int main(){
//	freopen("test.in","r",stdin);	
	ll n,sum=0; cin>>n; n=1<<n;
	p.resize(n),g.resize(n),f.resize(n);
	for(int i=0;i<n;i++) cin>>p[i],sum+=p[i];
	sum=ksm(sum,mod-2);
	for(int i=0;i<n;i++) p[i]=p[i]*sum%mod;
	for(int i=0;i<n;i++) g[i]=mod-1;
	p[0]=(p[0]+mod-1)%mod; g[0]=(g[0]+n)%mod;
	FWT(p,Cxor,n),FWT(g,Cxor,n);
	for(int i=1;i<n;i++) f[i]=g[i]*ksm(p[i],mod-2)%mod,(f[0]+=f[i])%=mod;
	f[0]=mod-f[0];
	FWT(f,Ixor,n);
	for(int i=0;i<n;i++) cout<<f[i]<<'\n';
}

CF449D Jzzhu and Numbers

目前看到的最简单的做法 戳这里

显然这是一个 and 意义下的背包
f i , j = f i − 1 , j + ∑ k & a i = j f i − 1 , k f_{i,j}=f_{i-1,j}+\sum_{k\&a_i=j}f_{i-1,k} fi,j=fi1,j+k&ai=jfi1,k
然后两边取 FWT ,做 n n n 遍卷积。

for(int i=1;i<=n;i++) {
    	memset(f,0,sizeof(f));
    	f[a[i]]=1;FWT(f,1);
    	for(re int j=0;j<len;j++) S[j]=S[j]+S[j]*f[j];
}
FWT(S,-1);

and 卷积的本质是超集和,所以 F i F_i Fi 不是 0 0 0 就是 1 1 1。你观察这个式子,每当 F j = 1 F_j=1 Fj=1 S j S_j Sj 就翻一倍,我们想知道 S j S_j Sj 翻了几倍其实就是询问在所有的 a i a_i ai 中有几个是 j j j 的超集。做一遍 F W T a n d FWTand FWTand 就能求出来了。

代码

#include <bits/stdc++.h>
#define N 1000006 
using namespace std;
inline int read() {
    char c=getchar();
	int x=0; while(c<'0'||x>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
typedef long long ll;
typedef vector<ll> vec;
const ll mod=1e9+7;
ll Cand[2][2]={{1,1},{0,1}},Iand[2][2]={{1,mod-1},{0,1}};
ll a[N],k2[N];
vec f;
void Fwt(vec &a,ll c[2][2],int limit) {
	while(a.size()<limit) a.push_back(0);
	for(int mid=1;mid<limit;mid<<=1)
		for(int j=0;j<limit;j+=(mid<<1))
			for(int k=0;k<mid;k++){
				ll x=a[j+k],y=a[j+k+mid];
				a[j+k]=(x*c[0][0]%mod+y*c[0][1]%mod)%mod;
				a[j+k+mid]=(x*c[1][0]%mod+y*c[1][1]%mod)%mod;
			}
}
int main() {
//	freopen("test.in","r",stdin);
	ll n,maxx=0;
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),maxx=max(maxx,a[i]);
    int limit=1; 
	while(limit<=maxx) limit<<=1;
	f.resize(limit);
    k2[0]=1;
    for(int i=1;i<=n;i++) 
		k2[i]=(k2[i-1]+k2[i-1])%mod;
    for(int i=1;i<=n;i++) f[a[i]]++;
    Fwt(f,Cand,limit);
    for(int i=0;i<limit;i++) f[i]=k2[f[i]];
    Fwt(f,Iand,limit);
    if(f[0]==k2[n]) cout<<f[0]-1;
    else cout<<f[0];
    return 0;
}

留坑2333(K进制的位运算卷积

posted @ 2022-10-10 20:18  缙云山车神  阅读(31)  评论(0编辑  收藏  举报