FWT学习笔记
参考:
- [快速沃尔什变换(FWT)学习笔记][1]
- [FWT 详解 知识点][2]
定义:
快速沃尔什变换(FWT)主要解决位运算卷积的问题。给定两个数组 (A) 和 (B) (长度为2的整数幂):
[C_k = sum_{i oplus j=k}A_i·B_i$$ 其中$oplus$可以是与,或,异或。FWT利用类似于FFT的想法,把 $A$ 和 $B$ 分别正变换,在一个好的复杂度得到 $A$ 与 $B$ 按位卷积的的正变换,最后再逆变换回来就是答案。
## **模版[Luogu4717]**
```c++
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
const int mod = 998244353;
const int inv2 = 499122177;
const int N = (1<<17);
inline int read() {
char c=getchar(); int x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
inline void write(int x) {
if(x>=10)write(x/10);
putchar('0'+x%10);
}
using namespace std;
inline int add(int x,int y) {
x+=y;
if(x>=mod)x-=mod;return x;
}
inline int sub(int x,int y) {
x-=y;
if(x<0)x+=mod;return x;
}
void FWT_or(int a[],int n,int on) {
for(int i=1;i<n;i<<=1) {
for(int j=0;j<n;j+=(i<<1)) {
for(int k=0;k<i;++k) {
int u=a[j+k], t=a[j+k+i];
a[j+k]=u;
if(on==1)a[j+k+i]=add(t,u);
else a[j+k+i]=sub(t,u);
}
}
}
}
void FWT_and(int a[],int n,int on) {
for(int i=1;i<n;i<<=1) {
for(int j=0;j<n;j+=(i<<1)) {
for(int k=0;k<i;++k) {
int u = a[j+k], t=a[j+k+i];
if(on==1) a[j+k]=add(u,t);
else a[j+k]=sub(u,t);
a[j+k+i]=t;
}
}
}
}
void FWT_xor(int a[],int n,int on) {
for(int i=1;i<n;i<<=1) {
for(int j=0;j<n;j+=(i<<1)) {
for(int k=0;k<i;++k) {
int u=a[j+k], t=a[j+k+i];
a[j+k]=add(u,t); a[j+k+i]=sub(u,t);
if(on==-1) {
a[j+k]=(ll)a[j+k]*inv2%mod;
a[j+k+i]=(ll)a[j+k+i]*inv2%mod;
}
}
}
}
}
int a[N],b[N],c[N],d[N];
int main() {
int n = read(), m = (1<<n);
rep(i,0,m-1) a[i]=read();
rep(i,0,m-1) b[i]=read();
memcpy(c,a,sizeof(c));
memcpy(d,b,sizeof(b));
FWT_or(c,m,1); FWT_or(d,m,1);
rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
FWT_or(c,m,-1);
rep(i,0,m-1)write(c[i]),putchar(' ');
putchar('
');
memcpy(c,a,sizeof(c));
memcpy(d,b,sizeof(b));
FWT_and(c,m,1); FWT_and(d,m,1);
rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
FWT_and(c,m,-1);
rep(i,0,m-1)write(c[i]),putchar(' ');
putchar('
');
memcpy(c,a,sizeof(c));
memcpy(d,b,sizeof(b));
FWT_xor(c,m,1); FWT_xor(d,m,1);
rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
FWT_xor(c,m,-1);
rep(i,0,m-1)write(c[i]),putchar(' ');
putchar('
');
return 0;
}
```
## **BZOJ4589[Hard Nim]**
题意:求长度为n,元素为小于m的质数,且异或和为0的方案数
做法:设 $f[i][j]$ 表示长度为i的序列异或和为j的方案数,则$f[i][x oplus y] = f[i-1][x]·f[1][y]$ 将$f[1]$内编号为素数的位置为1,其余为0,那么$f[i][k] = sum _{x oplus y=k} f[i-1][x]·f[1][y]$,答案就是要求 $f[1]$ 卷积 $n$ 次的f[n][0],又因为每次乘的都是 $f[1]$ 所以可以先对 $f[1]$ 求FWT正变换,分别n次幂后再加起来,最后逆变换出答案向量 $f[n]$
```c++
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define pb push_back
typedef long long ll;
const int N = 65537<<1;
const ll mod = 1e9 + 7;
using namespace std;
int m, p[N], notp[N];
ll n,a[N],inv2;
ll q_pow(ll a,ll b) {
ll ans = 1;
while(b) {
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
inline ll add(ll x,ll y) {
x+=y;
if(x>=mod)x-=mod;
return x;
}
inline ll sub(ll x,ll y) {
x-=y;
if(x<0)x+=mod;
return x;
}
void FWT_xor(ll a[],int n,int on) {
for(int i=1;i<n;i<<=1) {
for(int j=0;j<n;j+=(i<<1)) {
for(int k=0;k<i;++k) {
ll u = a[j+k], t = a[j+k+i];
a[j+k]=add(u,t); a[j+k+i]=sub(u,t);
if(on==-1) {
a[j+k]=a[j+k]*inv2%mod;
a[j+k+i]=a[j+k+i]*inv2%mod;
}
}
}
}
}
void init() {
inv2 = q_pow(2,mod-2);
notp[1] = 1;
for(int i=2;i<=5e4;++i) {
if(!notp[i])p[++p[0]]=i;
for(int j=1;j<=p[0]&&p[j]*i<=5e4;++j) {
notp[p[j]*i] = 1;
if(i%p[j]==0)break;
}
}
}
int main() {
init();
while(~scanf("%lld%d",&n,&m)) {
memset(a,0,sizeof(a));
rep(i,1,m) a[i] = (!notp[i]);
int len;
for(len=1;len<=m;len<<=1);
FWT_xor(a,len,1);
rep(i,0,len-1) a[i]=q_pow(a[i],n);
FWT_xor(a,len,-1);
printf("%lld
",a[0]%mod);
}
return 0;
}
```
[1]:https://www.cnblogs.com/RabbitHu/p/9182047.html
[2]:https://blog.csdn.net/neither_nor/article/details/60335099]