P4717

【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

我决定直接记掉代码加结论走人。

这个东西是计算:

$$h_i=\sum_{j\oplus k=i}f_j\times g_k$$

这种卷积的。

其中 $\oplus$ 为某种位运算。

我们仍然希望能像 DFT 和 IDFT 那样,将这个位运算的卷积换成点积。并且这个变换是线性的。

所以我们不妨设数列 $\mathbf{f}$ 的 DWT 满足:

$$\hat{f_i}=\sum_{j=0}^{n-1}c(i,j)f_j$$

(仿照 DFT 的定义)称 $\mathbf{\hat{f}}$ 为 $\mathbf{f}$ 的 DWT。

我们不像 DFT 有单位根那样,这个 $c(i,j)$ 我们并不知道。

好像会有 $\hat{f_i}\cdot \hat{g_i}=\hat{h_i}$。

至于 DWT 这个过程如何分治。

我很难解释这个过程。因为这个东西本来就比较直觉且抽象,想给出一个比较数学的证明还挺那啥的。。。

技不如人,给出结论。。。

$$\hat{f_{0x}}=c(0,0)\hat{f_{0x}}+c(0,1)\hat{f_{1x}}$$

$$\hat{f_{1x}}=c(1,0)\hat{f_{0x}}+c(1,1)\hat{f_{1x}}$$

这里 $x$ 是一个二进制数,$0x$ 表示把 $0$ 接在 $x$ 前面组成二进制数,$1x$ 同理。

最后 $c$ 的构造有不同的矩阵。IDWT 就是对 DWT 的矩阵求个逆。

1. $\operatorname{Or}$ 卷积

矩阵是这个:

$$\begin{bmatrix}1 & 0 \\ 1 & 1\end{bmatrix}$$

它的逆是这个:

$$\begin{bmatrix}1 & 0 \\ -1 & 1\end{bmatrix}$$

2. $\operatorname{And}$ 卷积

矩阵是这个:

$$\begin{bmatrix}1 & 1 \\ 0 & 1\end{bmatrix}$$

它的逆是这个:

$$\begin{bmatrix}1 & -1 \\ 0 & 1\end{bmatrix}$$

3. $\operatorname{Xor}$ 卷积

矩阵是这个:

$$\begin{bmatrix}1 & 1 \\ 1 & -1\end{bmatrix}$$

它的逆是这个:

$$\begin{bmatrix}\dfrac{1}{2} & \dfrac{1}{2} \\ \dfrac{1}{2} & -\dfrac{1}{2}\end{bmatrix}$$

然后 $2$ 在 $\pmod{998244353}$ 下的逆元是 $499122177$。其他情况的逆元可以自己两分钟左右写个快速幂算出来。

时间复杂度 $O(n2^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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define Cpy(f,g,n) memcpy(f,g,sizeof(ll)*(n))
using namespace std;

const ll N=1.35e5,mo=998244353,inv2=499122177;
const ll cor[2][2]={{1,0},{1,1}},icor[2][2]={{1,0},{mo-1,1}},
cand[2][2]={{1,1},{0,1}},icand[2][2]={{1,mo-1},{0,1}},
cxor[2][2]={{1,1},{1,mo-1}},icxor[2][2]={{inv2,inv2},{inv2,mo-inv2}};

ll n;

ll f[N+5],g[N+5],a[N+5],b[N+5];

inline void FWT(ll *f,const ll c[2][2],ll n) {
for(ll p=2;p<=n;p<<=1) {
ll len=p>>1;
for(ll k=0;k<n;k+=p) {
for(ll l=k;l<k+len;l++) {
ll t=f[l];
f[l]=(c[0][0]*f[l]+c[0][1]*f[l+len])%mo;
f[l+len]=(c[1][0]*t+c[1][1]*f[l+len])%mo;
}
}
}
}

inline void Bitmul(ll *f,ll *g,const ll c[2][2],const ll ic[2][2],ll n) {
FWT(f,c,n);FWT(g,c,n);for(ll i=0;i<n;i++) f[i]=f[i]*g[i]%mo;FWT(f,ic,n);
}

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<48||ch>57) {if(ch==45) f=-f;ch=getchar();}
while(ch>=48&&ch<=57) {ret=(ret<<3)+(ret<<1)+ch-48;ch=getchar();}
return ret*f;
}

inline void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {do{buf[++len]=x%10+48;x/=10;}while(x);}
else {putchar(45);do{buf[++len]=-(x%10)+48;x/=10;}while(x);}
while(len>=0) putchar(buf[len--]);
}

inline void writes(ll x) {write(x);putchar(32);}

int main() {

n=1<<read();

for(ll i=0;i<n;i++) f[i]=read();
for(ll i=0;i<n;i++) g[i]=read();

Cpy(a,f,n);Cpy(b,g,n);
Bitmul(a,b,cor,icor,n);
for(ll i=0;i<n;i++) writes(a[i]);putchar(10);

Cpy(a,f,n);Cpy(b,g,n);
Bitmul(a,b,cand,icand,n);
for(ll i=0;i<n;i++) writes(a[i]);putchar(10);

Cpy(a,f,n);Cpy(b,g,n);
Bitmul(a,b,cxor,icxor,n);
for(ll i=0;i<n;i++) writes(a[i]);putchar(10);

return 0;
}