FWT学习笔记。
由于一些奇怪的xp要求,在运算时我们需要位运算卷积。
于是就有了FWT!
FWT是用来处理一些位运算多项式的卷积,然后依然是分治形式,形如\(or,and,xor\)等位运算,先用个$\otimes $表示,大概就是
\[c_k=\sum\limits_{i\otimes j=k}a_i*b_j
\]
这个亚子。
or卷积
\[c_k=\sum\limits_{i|j=k}a_i*b_j
\]
写成向量就是:
\[A|B=(\sum\limits_{i|j=0}A_i*B_j,\sum\limits_{i|j=1}A_i*b_j,...,)
\]
满足交换律和结合律
对于或卷积有:
\[FWT(A)=
\begin{cases}
(FWT(A_0),FWT(A_0+A_1)) &n>0\\ A &n=0
\end{cases}
\]
关于证明:
别问,问就是不会证
and卷积
\[c_k=\sum\limits_{i\&j=k}A_i*B_j
\]
写成向量就是:
\[A\&B=(\sum\limits_{i\&j=0}A_i*B_j,\sum\limits_{i\&j=1}A_i*b_j,...,)
\]
依然满足交换律和结合律
对于与卷积有:
\[FWT(A)=
\begin{cases}
(FWT(A_0+A_1),FWT(A_1)) &n>0\\A &n=0
\end{cases}
\]
xor卷积
由于^符号过于难打于是用\(\otimes\)表示
\[c_k=\sum\limits_{i\otimes j=k}A_i*B_j
\]
然后对于FWT(A):
\[FWT(A)=
\begin{cases}
(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))&n>0\\
A&n=0
\end{cases}
\]
IFWT
现在我们可以在\(O(n\log n)\)时间内求出\(FWT(A\oplus B)\),然后还需要再把它变回去,也就是IFWT。
反着做一遍就行力:
对于or:
\[IFWT(A)=(IFWT(A_0),IFWT(A_1)-IFWT(A_0))
\]
对于and:
\[IFWT(A)=(IFWT(A_0)-IFWT(A_1),IFWT(A_1))
\]
对于xor:
\[IFWT(A)=(\frac{IFWT(A_0)+IFWT(A_1)}{2},\frac{IFWT(A_0)-IFWT(A_1)}{2})
\]
然后放个码,匹配了一下之前FFTNTT的码风:
inline void FWT_or(int *a,int opt){
for(int mid=1;mid<lim;mid<<=1)
for(int j=0;j<lim;j+=(mid<<1))
for(int k=0;k<mid;k++)
if(opt==1)a[j+mid+k]+=a[j+k],a[j+mid+k]-=a[j+mid+k]>=mod?mod:0;
else a[j+mid+k]-=a[j+k],a[j+mid+k]+=a[j+mid+k]<0?mod:0;
}
inline void FWT_and(int *a,int opt){
for(int mid=1;mid<lim;mid<<=1)
for(int j=0;j<lim;j+=(mid<<1))
for(int k=0;k<mid;k++)
if(opt==1)a[j+k]+=a[j+mid+k],a[j+k]-=a[j+k]>=mod?mod:0;
else a[j+k]-=a[j+mid+k],a[j+k]+=a[j+k]<0?mod:0;
}
inline void FWT_xor(int *a,int opt){
for(int mid=1;mid<lim;mid<<=1)
for(int j=0;j<lim;j+=(mid<<1))
for(int k=0;k<mid;k++){
int x=a[j+k],y=a[j+mid+k];
a[j+k]=(x+y)%mod,a[j+mid+k]=(x-y+mod)%mod;
if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%mod,a[j+mid+k]=1ll*a[j+mid+k]*inv2%mod;
}
}
对于子集和可以直接:
inline void FWT(int *a){
for(int mid=1;mid<lim;mid<<=1)
for(int j=0;j<lim;j+=(mid<<1))
for(int k=0;k<mid;k++)
a[j+mid+k]+=a[j+k],a[j+mid+k]-=a[j+mid+k]>=mod?mod:0;
}
Everything that kills me makes me feel alive.