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=0n−1c(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=0∑n−1c(i,j)A[j]k=0∑n−1c(i,k)B[k]=p=0∑n−1c(i,p)C[p]j=0∑n−1k=0∑n−1c(i,j)c(i,k)A[j]B[k]=p=0∑n−1c(i,p)C[p]
根据
A
∗
B
=
C
A*B=C
A∗B=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]=t1⊕t2∑A[t1]B[t2]p=0∑n−1c(i,p)C[P]=p=0∑n−1c(i,p)t1⊕t2=p∑A[t1]B[t2]j=0∑n−1k=0∑n−1c(i,j)c(i,k)A[i]B[k]=p=0∑n−1t1⊕t2=p∑c(i,t1⊕t2)A[t1]B[t2]j=0∑n−1k=0∑n−1c(i,j)c(i,k)A[i]B[k]=t1=0∑n−1t2=0∑n−1c(i,t1⊕t2)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,j⊕k) 。
则有 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,jt⊕kt)→c(i,j)c(i,k)=c(i,j⊕k) 。
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=0∑n−1c(i,j)A[j]=j=0∑(n/2)−1c(i,j)A[j]+j=(n/2)∑n−1c(i,j)A[i]=j=0∑(n/2)−1c(i0,j0)c(i′,j′)A[j]+j=(n/2)∑n−1c(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)∑n−1c(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=[1−101] -
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=[10−11] -
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=[111−1] c1=[1/21/21/2−1/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+i∑pifx⊕i,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,...,p2N−1)⋅(f0,f1,...,f2N−1)=(y,f1−1,...,f2N−1)
因为
∑
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=02N−1pi×∑j=02N−1fj=∑i=12N−1(fi−1)+y 所以
y
=
f
0
+
2
N
−
1
y=f_0+2^N-1
y=f0+2N−1 。
那么
(
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)
(p0−1,p1,...,p2N−1)⋅(f0,f1,...,f2N−1)=(2N−1,−1,−1,...,−1)
进行
F
W
T
FWT
FWT,可以得到
P
i
⋅
F
i
=
X
i
P_i\cdot F_i=X_i
Pi⋅Fi=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=02N−1(−1)∣i and j∣xi,发现仅有
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=2N∑i=02N−1Fi=0,因此
F
i
=
−
∑
i
=
1
2
N
−
1
F
i
F_i=-\sum_{i=1}^{2^N-1}F_i
Fi=−∑i=12N−1Fi 。最后一遍逆变换就能得到答案了。
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=fi−1,j+k&ai=j∑fi−1,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;
}