# 快速沃尔什变换(FWT)

## 2介绍

#### 2.2文章中有可能用到的符号&特殊说明

\begin{aligned} C(x)&=A(x)+B(x)\\ &=(a_0,a_1,a_2···a_{n-1})+(b_0,b_1,b_2···b_{n-1})\\ &=(a_0+b_0,a_1+b_1,a_2+b_2···a_{n-1}+b_{n-1})\\ \end{aligned}

\begin{aligned} C(x)&=A(x)-B(x)\\ &=(a_0,a_1,a_2···a_{n-1})-(b_0,b_1,b_2···b_{n-1})\\ &=(a_0-b_0,a_1-b_1,a_2-b_2···a_{n-1}-b_{n-1})\\ \end{aligned}

\begin{aligned} C(x)&=A(x)*B(x)\\ &=(a_0,a_1,a_2···a_{n-1})*(b_0,b_1,b_2···b_{n-1})\\ &=(a_0*b_0,a_1*b_1,a_2*b_2···a_{n-1}*b_{n-1})\\ \end{aligned}

\begin{aligned} C(x)&=A(x)@B(x)\\ &=(a_0,a_1,a_2···a_{n-1})@(b_0,b_1,b_2···b_{n-1})\\ &=(\sum _{i@j=0}a_i*b_j,\sum _{i@j=1}a_i*b_j,\sum _{i@j=2}a_i*b_j···\sum _{i@j=n-1}a_i*b_j)\\ \end{aligned}

\begin{aligned} \sum _{i@j=x}(a_i+b_i)*c_j&=\sum _{i@j=x}(a_i*c_j+b_i*c_j)\\ &=\sum _{i@j=x}a_i*c_j+\sum _{i@j=x}b_i*c_j \end{aligned}

\begin{aligned} (A,B)&=((a_0,a_1,a_2···a_{x-1}),(b_0,b_1,b_2···b_{y-1}))\\ &=(a_0,a_1,a_2···a_{x-1},b_0,b_1,b_2···b_{y-1})\\ \end{aligned}

## 3实现

update：这里的前两个(and/or)更严格意义上来说是FMT，本篇博客提供FMT本身的相关内容，另一篇博客提供FMT的更多小应用

#### 3.1or运算（本质FMT）

##### 3.1.1构造

\begin{aligned} FWT(A)=(\sum_{i|0=0}a_i,\sum_{i|1=1}a_i,\sum_{i|2=2}a_i···\sum_{i|(n-1)=(n-1)}a_i) \end{aligned}

\begin{aligned} FWT(A)*FWT(B)&=(\sum_{i|0=0}a_i,\sum_{i|1=1}a_i,\sum_{i|2=2}a_i···\sum_{i|(n-1)=(n-1)}a_i)*(\sum_{i|0=0}b_i,\sum_{i|1=1}b_i,\sum_{i|2=2}b_i···\sum_{i|(n-1)=(n-1)}b_i)\\ &=((\sum_{i|0=0}a_i)*(\sum_{j|0=0}b_j),(\sum_{i|1=1}a_i)*(\sum_{j|1=1}b_j),(\sum_{i|2=2}a_i)*(\sum_{j|2=2}b_j)···(\sum_{i|(n-1)=(n-1)}a_i)*(\sum_{j|(n-1)=(n-1)}b_j))\\ &=(\sum_{i|j|0=0}a_i*b_j,\sum_{i|j|1=1}a_i*b_j,\sum_{i|j|2=2}a_i*b_j···\sum_{i|j|(n-1)=(n-1)}a_i*b_j)\\ &=(\sum_{k|0=0}\sum_{i|j=k}a_i*b_j,\sum_{k|1=1}\sum_{i|j=k}a_i*b_j,\sum_{k|2=2}\sum_{i|j=k}a_i*b_j···\sum_{k|(n-1)=(n-1)}\sum_{i|j=k}a_i*b_j)\\ &=FWT(A|B) \end{aligned}

• 问题一：
已知一个多项式A，求FWT(A)
即FWT
• 问题二：
已知FWT(A)，求A
即IFWT

• 已知A,B
• FWT，求出FWT(A),FWT(B)
• 对应系数相乘，求出FWT(A|B)
• IFWT，求出A|B
##### 3.1.2计算

$FWT(A)=\begin{cases} (FWT(A_0),FWT(A_0+A_1))(n\ne1)\\ A(n=1) \end{cases}$

$\because FWT(A)_0=FWT(A_0)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(FWT(A)_0)\\ \because FWT(A)_1=FWT(A_0)+FWT(A_1)\\ \therefore A_1= IDFT(FWT(A_1))=IDFT(FWT(A)_1-FWT(A)_0)$

$IFWT(A)=\begin{cases} (IFWT(A_0),IFWT(A_1-A_0))(n\ne1)\\ A(n=1) \end{cases}$

inline void FWT(LL*A,const int fla)
{
for(rg int i=1;i<lenth;i<<=1)
for(rg int j=0;j<lenth;j+=(i<<1))
for(rg int k=0;k<i;k++)
A[j+k+i]+=A[j+k]*fla;
}


#### 3.2and运算（本质FMT）

##### 3.2.1构造

and其实和or差的不多

\begin{aligned} FWT(A)=(\sum_{i\&0=0}a_i,\sum_{i\&1=1}a_i,\sum_{i\&2=2}a_i···\sum_{i\&(n-1)=(n-1)}a_i) \end{aligned}

\begin{aligned} FWT(A)*FWT(B)&=(\sum_{i\&0=0}a_i,\sum_{i\&1=1}a_i,\sum_{i\&2=2}a_i···\sum_{i\&(n-1)=(n-1)}a_i)*(\sum_{i\&0=0}b_i,\sum_{i\&1=1}b_i,\sum_{i\&2=2}b_i···\sum_{i\&(n-1)=(n-1)}b_i)\\ &=((\sum_{i\&0=0}a_i)*(\sum_{j\&0=0}b_j),(\sum_{i\&1=1}a_i)*(\sum_{j\&1=1}b_j),(\sum_{i\&2=2}a_i)*(\sum_{j\&2=2}b_j)···(\sum_{i\&(n-1)=(n-1)}a_i)*(\sum_{j\&(n-1)=(n-1)}b_j))\\ &=(\sum_{i\&j\&0=0}a_i*b_j,\sum_{i\&j\&1=1}a_i*b_j,\sum_{i\&j\&2=2}a_i*b_j···\sum_{i\&j\&(n-1)=(n-1)}a_i*b_j)\\ &=(\sum_{k\&0=0}\sum_{i\&j=k}a_i*b_j,\sum_{k\&1=1}\sum_{i\&j=k}a_i*b_j,\sum_{k\&2=2}\sum_{i\&j=k}a_i*b_j···\sum_{k\&(n-1)=(n-1)}\sum_{i\&j=k}a_i*b_j)\\ &=FWT(A\&B) \end{aligned}

##### 3.2.2计算

$FWT(A)=\begin{cases} (FWT(A_0+A_1),FWT(A_1))(n\ne1)\\ A(n=1) \end{cases}$

$\because FWT(A)_0=FWT(A_0)+FWT(A_1)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(FWT(A)_0-FWT(A)_1)\\ \because FWT(A)_1=FWT(A_1)\\ \therefore A_1= IDFT(FWT(A_1))=IDFT(FWT(A)_1)$

$IFWT(A)=\begin{cases} (IFWT(A_0-A_1),IFWT(A_1))(n\ne1)\\ A(n=1) \end{cases}$

inline void FWT(LL*A,const int fla)
{
for(rg int i=1;i<lenth;i<<=1)
for(rg int j=0;j<lenth;j+=(i<<1))
for(rg int k=0;k<i;k++)
A[j+k]+=A[j+k+i]*fla;
}


#### 3.3xor运算（本质FWT）

##### 3.3.1构造

update：这是新版本，比较清晰易懂：

$popcount(x)$等于x在二进制下1的数量，我在下面简写为$pc(x)$

$FWT(A)=(\sum (-1)^{pc(i\&0)}a_i,\sum (-1)^{pc(i\&1)}a_i···\sum (-1)^{pc(i\&(n-1))}a_i)$

\begin{aligned} FWT(A)*FWT(B)&=(\sum (-1)^{pc(i\&0)}a_i,\sum (-1)^{pc(i\&1)}a_i···\sum (-1)^{pc(i\&(n-1))}a_i)*(\sum (-1)^{pc(i\&0)}b_i,\sum (-1)^{pc(i\&1)}b_i···\sum (-1)^{pc(i\&(n-1))}b_i)\\ &=((\sum (-1)^{pc(i\&0)}a_i)*((\sum (-1)^{pc(j\&0)}b_j)),(\sum (-1)^{pc(i\&1)}a_i)*(\sum (-1)^{pc(j\&1)}b_j)···(\sum (-1)^{pc(i\&(n-1))}a_i)*(\sum (-1)^{pc(j\&(n-1))}b_j))\\ &=(\sum (-1)^{pc(i\oplus j\&0)}a_i*b_j,(\sum (-1)^{pc(i\oplus j\&1)}a_i*b_j···(\sum (-1)^{pc(i\oplus j\&(n-1))}a_i*b_j)\\ &=(\sum (-1)^{pc(k\&0)}\sum _{i\oplus j=k}a_i*b_j,\sum (-1)^{pc(k\&1)}\sum _{i\oplus j=k}a_i*b_j···\sum (-1)^{pc(k\&(n-1))}\sum _{i\oplus j=k}a_i*b_j)\\ &=FWT(A\oplus B) \end{aligned}

$popcount(x)$等于x在二进制下1的数量，我在下面简写为$pc(x)$

$FWT(A)=(\sum pc(i\&0)mod\ 2==0? a_i:-a_i,\sum pc(i\&1)mod\ 2==0? a_i:-a_i,\sum pc(i\&2)mod\ 2==0? a_i:-a_i···\sum pc(i\&(n-1))mod\ 2==0? a_i:-a_i)$

\begin{aligned} FWT(A)*FWT(B)&=(\sum pc(i\&0)mod\ 2==0? a_i:-a_i,\sum pc(i\&1)mod\ 2==0? a_i:-a_i,\sum pc(i\&2)mod\ 2==0? a_i:-a_i···\sum pc(i\&(n-1))mod\ 2==0? a_i:-a_i)*(\sum pc(i\&0)mod\ 2==0? b_i:-b_i,\sum pc(i\&1)mod\ 2==0? b_i:-b_i,\sum pc(i\&2)mod\ 2==0? b_i:-b_i···\sum pc(i\&(n-1))mod\ 2==0? b_i:-b_i)\\ &=((\sum pc(i\&0)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&0)mod\ 2==0? b_j:-b_j),(\sum pc(i\&1)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&1)mod\ 2==0? b_j:-b_j),(\sum pc(i\&2)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&2)mod\ 2==0? b_j:-b_j)···(\sum pc(i\&(n-1))mod\ 2==0? a_i:-a_i)*(\sum pc(j\&(n-1))mod\ 2==0? b_j:-b_j))\\ &=(\sum pc((i\oplus j)\&0)mod\ 2==0? a_i*b_j:-a_i*b_j,\sum pc((i\oplus j)\&1)mod\ 2==0? a_i*b_j:-a_i*b_j,\sum pc((i\oplus j)\&2)mod\ 2==0? a_i*b_j:-a_i*b_j···\sum pc((i\oplus j)\&(n-1))mod\ 2==0? a_i*b_j:-a_i*b_j)\\ &=(\sum pc(k\&0)mod\ 2==0?\sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j,\sum pc(k\&1)mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j,\sum pc(k\&2)mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j···\sum pc(k\&(n-1))mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j)\\ &=FWT(A\oplus B) \end{aligned}

##### 3.3.2计算

$FWT(A)=\begin{cases} (FWT(A_0+A_1),FWT(A_0-A_1))(n\ne1)\\ A(n=1) \end{cases}$

$\because FWT(A)_0=FWT(A_0)+FWT(A_1)，FWT(A)_1=FWT(A_0)-FWT(A_1)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(\frac {FWT(A)_0+FWT(A)_1}2)，A_1= IDFT(FWT(A_1))=IDFT(\frac {FWT(A)_0-FWT(A)_1}2)$

$IFWT(A)=\begin{cases} (\frac {IFWT(A_0)+IFWT(A_1)}{2},\frac {IFWT(A_0)-IFWT(A_1)}{2})(n\ne1)\\ A(n=1) \end{cases}$

inline void FWT(LL*A,const int fla)
{
for(rg int i=1;i<lenth;i<<=1)
for(rg int j=0;j<lenth;j+=(i<<1))
for(rg int k=0;k<i;k++)
{
const int x=A[j+k],y=A[j+k+i];
A[j+k]=x+y;
A[j+k+i]=x-y;
if(fla==-1)A[j+k]/=2,A[j+k+i]/=2;
}
}


inline void FWT(LL*A,const int fla)
{
for(rg int i=1;i<lenth;i<<=1)
for(rg int j=0;j<lenth;j+=(i<<1))
for(rg int k=0;k<i;k++)
{
const int x=A[j+k],y=A[j+k+i];
A[j+k]=x+y;
A[j+k+i]=x-y;
}
if(fla==-1)
for(rg int i=0;i<lenth;i++)
A[i]/=lenth;
}


## 4总结

#include<cstdio>
#include<cctype>
#include<algorithm>
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
const int mod=998244353,lenth=524288;
int n,a[lenth],pc[lenth],val,f[lenth];
inline void FWT(int *A)
{
for(rg int i=1;i<lenth;i<<=1)
for(rg int j=0;j<lenth;j+=i<<1)
for(rg int k=0;k<i;k++)
{
const int x=A[j+k],y=A[j+k+i];
A[j+k]=(x+y)%mod;
A[j+k+i]=(x+mod-y)%mod;
}
}
int main()
{
a[0]=1;
FWT(a);
for(rg int i=1;i<lenth;i++)pc[i]=pc[i^(i&-i)]+1;
for(rg int i=0;i<lenth;i++)f[i]=1;
for(rg int tim=0;tim<=19;tim++)
{
LL calc=0;
for(rg int i=0;i<lenth;i++)calc+=(pc[i&val]&1)?-f[i]:f[i];
calc%=mod;
if(calc)
{
print(n-tim);
return 0;
}
for(rg int i=0;i<lenth;i++)f[i]=(LL)f[i]*a[i]%mod;
}
return 0;
}