FWT学习笔记

FWT学习笔记


参考:

  1. [快速沃尔什变换(FWT)学习笔记][1]
  2. [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('\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]$ ```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\n",a[0]%mod); } return 0; } ``` [1]:https://www.cnblogs.com/RabbitHu/p/9182047.html [2]:https://blog.csdn.net/neither_nor/article/details/60335099\]

posted @ 2018-08-14 21:33  RRRR_wys  阅读(325)  评论(0编辑  收藏  举报