跳到主要内容

快速沃尔什变换

参考资料

简介

快速沃尔什变换(Fast Walsh–Hadamard Transform,FWT)在 O(nlogn)O(n\log n) 内计算两个序列的 位运算卷积 ck=iopj=kaibjc_k=\sum_{i\operatorname{op}j=k}a_ib_j,其中 op\operatorname{op} 为按位或、与或异或。与 FFT 类似,它把序列正变换到「点值」、逐位相乘、再逆变换还原;或、与、异或三种运算对应不同的变换,但都能用分治在 O(nlogn)O(n\log n) 内完成。

实现

分治递归

按最高位把序列分成两半递归,再按变换规则合并。

191 Bcpp
void fmt_or(ll *a,int n,int type)
{
if(n==1)return;
int mid=n>>1;
ll *a0=a,*a1=a+mid;
fmt_or(a0,mid,type);
fmt_or(a1,mid,type);
for(int i=0;i<mid;i++)a1[i]=(a1[i]+a0[i]*type+mod)%mod;
}

倍增迭代

自底向上按位合并,避免递归、常数更小,是常用写法。

181 Bcpp
void fmt_or(ll *a,int n,int type)
{
for(int k=1;k<n;k<<=1)
{
for(int i=0;i<n;i+=k<<1)
{
for(int j=0;j<k;j++)
{
a[i+j+k]=(a[i+j+k]+a[i+j]*type+mod)%mod;
}
}
}
}

高维前缀和

或、与卷积本质是子集和与超集和,可直接用高维前缀和(SOS DP)按位逐维累加实现。

141 Bcpp
void fmt_or(ll *a,int n,int type)
{
for(int i=1;i<n;i<<=1)
{
for(int j=0;j<n;j++)
{
if(i&j)a[j]=(a[j]+a[i^j]*type+mod)%mod;
}
}
}

例题

给定长度为 2n2^n 的两个序列 A,BA,B,设:

Ck=ij=kAi×BjC_k=\sum_{i\oplus j=k}A_i\times B_j

分别当 \oplusor,and,xor\operatorname{or},\operatorname{and},\operatorname{xor} 时求出 CC

给定两个长度为 2n2^n 的序列 a0,a1,,a2n1a_0,a_1,\dots,a_{2^n-1}b0,b1,,b2n1b_0,b_1,\dots,b_{2^n-1},你需要求出一个序列 c0,c1,,c2n1c_0,c_1,\dots,c_{2^n-1},其中 ckc_k 满足:

ck=i&j=0ij=kaibjc_k=\sum_{\substack{{i \& j=0}\\{i\mid j=k}}} a_i b_j

其中 \mid 表示按位或,&\& 表示按位与。

答案对 109+910^9+9 取模。