快速沃尔什变换
快速沃尔什变换核心解决的问题是, 对于两个长度为 、从 开始编号的数组 和 , 在 的时间内求出下述定义的数组
其中 为位运算 中的一个 ,记为
接下来 表示算术加法, 表示算术乘法. 如果运算符两边是数组,则表示下标对应的两项间做运算.
和 FFT 类似,为了解决这个问题,构造一个变换 出来使得
同时要存在一个逆变换 使得
前人已经推出了 是怎样一个变换. 设 的长度为 ,不足 的部分用 补全. 用 表示 的前 项(即下标最高位为 的项), 表示 的后 项(即下标最高位为 的项). 表示 拼接而成的新数组.
- 异或
- 按位与
- 按位或
模板
(待整理)
void FWT(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
//xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;
//and:a[i+j]=x+y;
//or:a[i+j+d]=x+y;
}
}
void UFWT(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
//xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
//and:a[i+j]=x-y;
//or:a[i+j+d]=y-x;
}
}
void solve(int a[],int b[],int n)
{
FWT(a,n);
FWT(b,n);
for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;
UFWT(a,n);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34