快速沃尔什变换
Tangninghaha · · 算法·理论
简介
快速沃尔什变换用于解决位运算卷积问题,此类问题形如如下形式:
对于给定的长度为
其中
直接朴素计算的时间复杂度为
具体来说,考虑类似于 FFT 的思想,分为 3 步:
-
将
a 和b 数组进行沃尔什变换,得到数组A 和B 。 -
将数组
A 和B 对位相乘,得到数组C 。 -
将
C 进行沃尔什逆变换,得到数组c 。
按位或变换
当运算为按位或运算时,可以设
给定
void orTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){ // 枚举考虑到第几个二进制位
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){ // 枚举该二进制位及更高位的值为 i+j
(a[i+j+k]+=a[i+j]*type)%=mod;
// 那么比枚举的位更低的位,若为 1,则可以合并原来为 0 的位
}
}
}
}
逆变换时带入 type 为 -1 即可。
按位与变换
同理或运算,类比可得,设
void andTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){
(a[i+j]+=a[i+j+k]*type)%=mod;
}
}
}
}
按位异或运算
定义运算
设
具体来说,
证明考虑
-
若
j 和k 这一位均为1 ,那么j 与k 异或后这一位为0 ,故右式中i 与j\oplus j 不会同时为1 ,贡献为0 。考虑左式,若
i 这一位为0 ,则两者均为0 ,不产生贡献;若i 这一位为1 ,左式中i\circ j 和i\circ k 都会因为多了一个同为1 的位数而奇偶性改变,异或结果为0 ,也不改变。
其它情况分类讨论,同理可得。
void xorTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){
(a[i+j]+=a[i+j+k])%=mod;
a[i+j+k]=(a[i+j]+mod-a[i+j+k]+mod-a[i+j+k])%mod;
(a[i+j]*=type)%=mod;
(a[i+j+k]*=type)%=mod;
if(a[i+j+k]<0)a[i+j+k]+=mod;
}
}
}
}
逆变换时 type=
下面给出 洛谷模板题 的代码
#include<bits/stdc++.h>
using namespace std;
using LL=long long ;
const int N=1.4e5,mod=998244353;
int n;
void orTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){
(a[i+j+k]+=a[i+j]*type)%=mod;
}
}
}
}
void andTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){
(a[i+j]+=a[i+j+k]*type)%=mod;
}
}
}
}
void xorTrans(LL *a,LL type){
for(int l=2;l<=n;l<<=1){
int k=l>>1;
for(int i=0;i<n;i+=l){
for(int j=0;j<k;++j){
(a[i+j]+=a[i+j+k])%=mod;
a[i+j+k]=(a[i+j]+mod-a[i+j+k]+mod-a[i+j+k])%mod;
(a[i+j]*=type)%=mod;
(a[i+j+k]*=type)%=mod;
if(a[i+j+k]<0)a[i+j+k]+=mod;
}
}
}
}
void cpy(LL *a,LL *b){
for(int i=0;i<n;++i)a[i]=b[i];
}
void print(LL *a){
for(int i=0;i<n;++i)printf("%lld ",a[i]);
putchar(10);
}
LL a[N],b[N];
int main(){
scanf("%d",&n);
n=1<<n;
for(int i=0;i<n;++i)scanf("%lld",&a[i]);
for(int i=0;i<n;++i)scanf("%lld",&b[i]);
static LL A[N],B[N];
cpy(A,a);
cpy(B,b);
orTrans(A,1);
orTrans(B,1);
for(int i=0;i<n;++i)A[i]=A[i]*B[i]%mod;
orTrans(A,mod-1);
print(A);
cpy(A,a);
cpy(B,b);
andTrans(A,1);
andTrans(B,1);
for(int i=0;i<n;++i)A[i]=A[i]*B[i]%mod;
andTrans(A,mod-1);
print(A);
cpy(A,a);
cpy(B,b);
xorTrans(A,1);
xorTrans(B,1);
for(int i=0;i<n;++i)A[i]=A[i]*B[i]%mod;
xorTrans(A,499122177);
print(A);
return 0;
}