FWT(快速沃尔什变换)简要讲解
作者:互联网
写在前面
本蒟蒻很菜,所以这篇博客依然几乎不会有证明
还是先说这东西是干什么的
平时我们所见的卷积是这样的:
\[
h(k) = \sum_{i + j = k} f(i) \cdot g(j)
\]
但是偶尔还会遇到条件中的加号变成其它符号的情况,\(FWT\)就是这个符号是按位与,或,异时快速求解\(h\)的
主要的三种情况
其实就是我们要构造一种变换\(tf\),使得\(tf(h) = tf(f) \cdot tf(g)\),其中点乘表示对应位相乘
或卷积(|)
即:
\[
h(k) = \sum_{i | j = k} f(i) \cdot g(j)
\]
我们可以构造变换\(FWT(A)\),使得\(FWT(A)_i = \sum_{j | i = i} A_j\)(我也不知道这个方法是怎么想出来的……)
但是验证它还是不难的,首先有这个:\(i | j | k = k \Rightarrow i | k = k, j | k = k\)
那么
\[
\begin{align}
FWT(h)_i = \sum_{j | i = i} h_j & = \sum_{j | i = i} \sum_{s | t = j} f(s) \cdot g(t) \\
& = \sum_{s | i = i} \sum_{t | i = i} f(s) \cdot g(t) \\
& = \sum_{s | i = i} f(s) \sum_{t | i = i} g(t) = FWT(f)_i \cdot FWT(g)_i
\end{align}
\]
然后问题变成了快速求\(FWT(A)\)
由于这个变换满足下面的等式:
\[
FWT(A) = (FWT(A_0), FWT(A_0) + FWT(A_1))
\]
其中\(A_0\)表示序列前半段,\(A1\)表示序列后半段,加号表示对应位置相加。简单理解一下就是根据定义,\(FWT(A)_i\)是\(i\)的所有子集\(j\)处\(A_j\)之和,而序列前半段的下标二进制最高位为\(0\),增加这一位并不会产生新的子集,后半段下标二进制最高位为\(1\),增加这一位后前面高位为\(0\)的部分也会成为新的子集
所以显然就可以每次将序列分成前后两段分别计算,然后合并即可
然后怎么逆变换呢,根据上面的正变换,不难发现就是减掉所有它的子集处的值就是了,可以得出:
\[
IFWT(A) = (IFWT(A_0), IFWT(A_1) - IFWT(A_0))
\]
与卷积(&)
与和或在各个方面都联系紧密,那么就可以类比或卷积,不难得出:
\[
\begin{align}
FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_1)) \\
IFWT(A) & = (IFWT(A_0) - IFWT(A_1), IFWT(A_1))
\end{align}
\]
异或卷积(^)
这个比较复杂,蒟蒻我并没能吃透,好像和奇偶性有关??
结论是:
\[
\begin{align}
FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_0) - FWT(A_1)) \\
IFWT(A) & = (\frac{IFWT(A_0) + IFWT(A_1)}{2}, \frac{IFWT(A_0) - IFWT(A_1)}{2})
\end{align}
\]
另外一个很少见的运算——同或(\(\odot\))
直接上结论了:
\[
\begin{align}
FWT(A) & = (FWT(A_1) - FWT(A_0), FWT(A_1) + FWT(A_0)) \\
IFWT(A) & = (\frac{IFWT(A_1) - IFWT(A_0)}{2}, \frac{IFWT(A_1) + IFWT(A_0)}{2})
\end{align}
\]
代码
模板题戳这里
因为没见到同或的题,也没有看见过同或的模板,下面的代码只有前三种运算QwQ
还有就是取模意义下除以\(2\)转换为乘上逆元QwQ
/*
type == 0 => or
type == 1 => and
type == 2 => xor
*/
void FWT(LL *arr, int n, int type) {
for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
for (int i = 0; i < (1 << n); i += len)
for (int j = 0; j < half; ++j)
if (type == 0) inc(arr[i + half + j], arr[i + j]);
else if (type == 1) inc(arr[i + j], arr[i + half + j]);
else {
LL a0 = arr[i + j], a1 = arr[i + half + j];
inc(arr[i + j], a1); dec(arr[i + half + j] = a0, a1);
}
}
void IFWT(LL *arr, int n, int type) {
for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
for (int i = 0; i < (1 << n); i += len)
for (int j = 0; j < half; ++j)
if (type == 0) dec(arr[i + half + j], arr[i + j]);
else if (type == 1) dec(arr[i + j], arr[i + half + j]);
else {
LL a0 = arr[i + j], a1 = arr[i + half + j];
arr[i + j] = (a0 + a1) * inv % mod;
arr[i + half + j] = (a0 - a1) * inv % mod;
if (arr[i + half + j] < 0) arr[i + half + j] += mod;
}
}
标签:简要,cdot,sum,IFWT,卷积,沃尔什,FWT,align 来源: https://www.cnblogs.com/Rhein-E/p/10502969.html