FWT学习笔记
参考:
- 快速沃尔什变换(FWT)学习笔记
- FWT 详解 知识点
定义:
快速沃尔什变换(FWT)主要解决位运算卷积的问题。给定两个数组 \(A\) 和 \(B\) (长度为2的整数幂):
\[C_k = \sum_{i \oplus j=k}A_i·B_i\] 其中\(\oplus\)可以是与,或,异或。FWT利用类似于FFT的想法,把 \(A\) 和 \(B\) 分别正变换,在一个好的复杂度得到 \(A\) 与 \(B\) 按位卷积的的正变换,最后再逆变换回来就是答案。
模版[Luogu4717]
#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('\n');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('\n');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('\n');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]\)
#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\n",a[0]%mod);}return 0;
}