版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ike940067893/article/details/85196898
FWT能解决什么
- 有的时候我们会遇到要求一类卷积,如下:
此处乘号为普通乘法, 表示一种位运算,如 与 或 异或 ^
打不了 ^ 啊…qwq
FWT思想
- 首先因为是位运算,所以需要按位分解。又因为是卷积的形式,联想到 中利用了一种分治优化降低时间复杂度,所以我们首先把多项式拓展到 的次幂长度,方便按位分治
-
的思想就是利用一种向量变换来简化运算,首先我们定义向量
(此处可理解为数组)的正变换为
的正变换为
,逆变换为
- 先拿
的情况举例,根据位运算常识有
- 所以令
则有 - 那么我们只需要求出 ,就能得到 ,然后通过逆变换求出
- 先拿
的情况举例,根据位运算常识有
变换与逆变换具体实现
- 像FFT一样,分治求 。拿 运算举例
- 将一个长度为 区间二分,那么左边和右边分别是最高位为 的数,此时递归处理左右两边。相当于先不考虑最高位,递归处理左右两边长度为 的答案
- 要想将两个区间合并,由于是 运算,两个数的与运算只会变小,那么只会是右边的区间对左边造成贡献
- 记左边处理出来的答案为 ,右边处理出来的答案为 ,合并后的答案为 , 与 的实际含义为
- 显然有
- 求逆变换 时有
- 于是我们就解决了与运算的问题,或运算可类比
异或卷积
- 异或 与其他两个有点不一样(毕竟 写不出来),需要多想一想
- 异或卷积基于以下原理
-
定义 和 之间的奇偶性为 中为1的位数的奇偶性,若为偶数则奇偶性是0,若为奇数则奇偶性是1。记作
-
令
就有了- 证明为
- 将 同时减去它们的相与的值 ,它们的相对奇偶性(可以理解吧)不变,减去后 在二进制下没有同时为 的位,所以异或可以直接相加
- 所以当 ,同时减去后奇偶性还是相等,那么 的奇偶性=两个相等的奇偶性加起来=0=
- 所以当 ,同时减去后奇偶性还是不等,那么 的奇偶性=两个不等的奇偶性加起来=1=
- 证毕(看懵逼的写两个二进制数来看看,很好理解的)
- 证明为
-
看看怎么分治,此处 与 的实际含义为
-
有
-
怎么想呢,分类讨论吧。由于:
- 对于左边区间的 ,根据 的定义,显然满足
-
而对于右边区间的
- 当 在左边区间, 的值一定和 的值相等。所以加上
- 当 在右边区间, 的值一定和 的值相反。所以减去
-
逆变换可自行推导(或看下方代码)
-
Luogu板题链接:P4717 【模板】快速沃尔什变换
写法跟FFT,NTT一模一样,还要更简(hao)单(bei)
AC code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1<<17;
const int mod = 998244353;
const int inv2 = 499122177;
int n, a[MAXN], b[MAXN];
int a1[MAXN], a2[MAXN];
inline void FWT_or(int arr[], const int& len, const int& flg)
{
register int x, y;
for(register int i = 2; i <= len; i<<=1)
for(register int j = 0; j < len; j += i)
for(register int k = j; k < j + i/2; ++k)
{
x = arr[k], y = arr[k + i/2];
if(~flg) arr[k + i/2] = (x + y) % mod;
else arr[k + i/2] = (y - x + mod) % mod;
}
}
inline void FWT_and(int arr[], const int& len, const int& flg)
{
register int x, y;
for(register int i = 2; i <= len; i<<=1)
for(register int j = 0; j < len; j += i)
for(register int k = j; k < j + i/2; ++k)
{
x = arr[k], y = arr[k + i/2];
if(~flg) arr[k] = (x + y) % mod;
else arr[k] = (x - y + mod) % mod;
}
}
inline void FWT_xor(int arr[], const int& len, const int& flg)
{
register int x, y;
for(register int i = 2; i <= len; i<<=1)
for(register int j = 0; j < len; j += i)
for(register int k = j; k < j + i/2; ++k)
{
x = arr[k], y = arr[k + i/2];
if(~flg) arr[k] = (x + y) % mod, arr[k + i/2] = (x - y + mod) % mod;
else arr[k] = (LL)(x + y) * inv2 % mod, arr[k + i/2] = (LL)(x - y + mod) * inv2 % mod;
}
}
inline void solve_or(const int& len)
{
memcpy(a1, a, sizeof a);
memcpy(a2, b, sizeof b);
FWT_or(a1, len, 1);
FWT_or(a2, len, 1);
for(int i = 0; i < len; ++i)
a2[i] = (LL)a1[i] * a2[i] % mod;
FWT_or(a2, len, -1);
for(int i = 0; i < len; ++i)
printf("%d%c", a2[i], i == len-1 ? '\n' : ' ');
}
inline void solve_and(const int& len)
{
memcpy(a1, a, sizeof a);
memcpy(a2, b, sizeof b);
FWT_and(a1, len, 1);
FWT_and(a2, len, 1);
for(int i = 0; i < len; ++i)
a2[i] = (LL)a1[i] * a2[i] % mod;
FWT_and(a2, len, -1);
for(int i = 0; i < len; ++i)
printf("%d%c", a2[i], i == len-1 ? '\n' : ' ');
}
inline void solve_xor(const int& len)
{
memcpy(a1, a, sizeof a);
memcpy(a2, b, sizeof b);
FWT_xor(a1, len, 1);
FWT_xor(a2, len, 1);
for(int i = 0; i < len; ++i)
a2[i] = (LL)a1[i] * a2[i] % mod;
FWT_xor(a2, len, -1);
for(int i = 0; i < len; ++i)
printf("%d%c", a2[i], i == len-1 ? '\n' : ' ');
}
int main ()
{
scanf("%d", &n); n = 1<<n;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int i = 0; i < n; ++i) scanf("%d", &b[i]);
solve_or(n);
solve_and(n);
solve_xor(n);
}