【模板】快速沃尔什变换(FWT)

P4717 【模板】快速沃尔什变换 (FWT)

给定长度为 \(2^n\) 两个序列 \(A,B\),设

\[C_i=\sum_{j\oplus k = i}A_j \times B_k \]

分别当 \(\oplus\) 是 or,and,xor 时求出 \(C\)\(n\le 17\)

参考资料

FWT?

a.cpp Accepted

由分治乘法的式子[1]1,有:

\[C_0=A_0*B_0 \\ C_1=(A_0+A_1)*(B_0+B_1)-C_0 \]

代码为:

inline static const void mulor(int*a,int*b,int*c,int lm){
    if(!(lm/=2))return void(*c=ll(*a)**b%mod);
    for(int i=0;i<lm;++i)reduce(a[i+lm]+=a[i]-mod),reduce(b[i+lm]+=b[i]-mod);
    mulor(a,b,c,lm),mulor(a+lm,b+lm,c+lm,lm);
    for(int i=0;i<lm;++i)reduce(c[i+lm]-=c[i]);
}

如何将“递归都改为循环”:考虑\(C\)的计算与\(A,B\)的处理是独立的。且展开之后发现循环顺序不相关,故有[2]2

inline void get() {
    for (int i = 0; i < n; i++) a[i] *= b[i];
}
inline void OR(modint *f, modint x = 1) {
    for (int o = 2, k = 1; o <= n; o <<= 1, k <<= 1)
        for (int i = 0; i < n; i += o)
            for (int j = 0; j < k; j++)
                f[i+j+k] += f[i+j] * x;
}

OR(a), OR(b), get(), OR(a, P - 1);

又由于,每层分治相当于沿着第\(k\)位做了个卷积[1],可以进一步简化为

void OR(int *a, bool flag) {
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < (1<<n); ++j) if ((j>>i&1) == 0) {
			int x = a[j], y = a[j+(1<<i)];
			if (flag) x = P-x;
			a[j+(1<<i)] = (y+x)%P;
		}
	}
}

FWT

为简单起见,只尝试说明异或卷积\(\bigoplus\)

定义\(C=A\bigoplus B\iff C_i=\sum_{j\oplus k}A_jB_k\)。若\(C\)的长度为\(2^k,k\ge1\),定义\(C_0,C_1\)分别为\(C\)的前半部分与后半部分。定义\((A,B)\)表示\(A\)\(B\)连接在一起的序列。定义\(C=A\operatorname{op}B\iff C_i=A_i\operatorname{op} B_i\),其中\(\operatorname{op}\)为二元运算(\(+,-,\times,\div\)):

理解:分治乘法

\(X_0=(A_0+A_1)\bigoplus(B_0+B_1),X_1=(A_0-A_1)\bigoplus(B_0-B_1)\)

\(C_0=A_0\bigoplus B_0+A_1\bigoplus B_1=\frac{X_0+X_1}{2}, C_1=A_0\bigoplus B_1+A_1\bigoplus B_0=\frac{X_0-X_1}{2}\)

故若需要求\(A\bigoplus B\),需要先求得\(X_0,X_1\)再合并。直接分治即可。

inline void doxor(int *a, int l, int mul = 1) {
  for (int i = 0; i < l; ++i) {
    int x = a[i], y = a[i+l];
    a[i] = (ll)(x+y)*mul%P;
    a[i+l] = (ll)(x-y+P)*mul%P;
  }
}
void xor(int *a, int *b, int *c, int l) {
  if (!(l>>=1)) {
    c[0] = (ll)a[0]*b[0]%P;
    return;
  }
  doxor(a, l), doxor(b, l);
  xor(a, b, c), xor(a+l, b+l, c+l);
  doxor(c, l, (P+1)/2);
}

理解:数学归纳法[6]6

\(A*B\)表示\(A\)\(B\)的某种卷积:

若存在递归定义的\(f,g\)使得对\(\forall A,B\)\(f(A)\times f(B)=f(A*B),g(f(A))=1\),则可以用分治发求出\(A*B\)

设函数\(f\)\(g\)满足:

\[f(A)=\begin{cases} A & \deg A=1 \\ (f(A_0+A_1),f(A_0-A_1)) & \deg A=2^k,k>0 \end{cases} \\ g(A)=\begin{cases} A & \deg A=1 \\ (g(A_0+A_1),g(A_0-A_1))/2 & \deg A=2^k,k>0 \end{cases} \]

若可以递归地定义:\(\oplus\)表示某二元位运算,\(\bigoplus\)表示该位运算的卷积。

若对长为\(2^0,2^1\)\(A,B\),有类似定义的\(f,g\)满足\(f(A)\times f(B)=f(A\bigoplus B),g(f(A))=A\),则可以证明对长度为\(2^k\)\(A,B\),均满足\(f(A)\times f(B)=f(A\bigoplus B),g(f(A))=A\)

\(n=2^0\),显然有\(f(A)\times f(B)=f(A\bigoplus B)\)

可以将 FWT 的过程看做一个线性变换的过程(左乘一个矩阵),则有\(f(A)+f(B)=f(A+B)\)

归纳法可证明对所有\(n=2^k,C=A\bigoplus B\),有\(f(C)=f(B)\times f(A)\)

\[f(A)=(f(A_0)+f(A_1),f(A_0)-f(A_1)) \\ f(B)=(f(B_0)+f(B_1),f(B_0)-f(B_1)) \\ C=A\bigoplus B=(A_0\bigoplus B_0+A_1\bigoplus B_1,A_0\bigoplus B_1+A_1\bigoplus B_0) \\ \begin{aligned} f(C) &= (f(C_0)+f(C_1),f(C_0)-f(C_1))\\ &\quad\because f(C_0)=f(A_0\bigoplus B_0+A_1\bigoplus B_1) =f(A_0)f(B_0)+f(A_1)f(B_1)\\ f(C) &= (f(A_0+A_1)\times f(B_0+B_1),f(A_0-A_1)\times f(B_0-B_1))\\ &= (f(A_0+A_1),f(A_0-A_1))\times (f(B_0+B_1),f(B_0-B_1))\\ &= f(A)\times f(B) \end{aligned} \]

现在只需要证明有\(g(f(A))=A\)即可。因为:

\[\begin{aligned} g(f(A))&=(g(f(A)_0+f(A)_1),g(f(A)_0-f(A)_1))/2 \\ &= (g(f(2A_0)),g(f(2A_1)))/2 \\ &= (A_0,A_1) = A \end{aligned} \]

由数学归纳法即证。

理解:多维FFT

对于异或来说,可以将FWT理解成一个多维的FFT,即对于一个


  1. 来自何爷爷的讲解 ↩︎

posted @ 2021-05-20 20:33  frank3215  阅读(128)  评论(0编辑  收藏  举报