FWT-快速沃尔什变换
FWT有啥用啊
我们知道,FFT
可以解决多项式的卷积,即
[C_k=sum_{i+j=k}A_i*B_j
]
如果将操作符换一下,换成集合运算符
比如
[C_k=sum_{i|j=k}A_i*B_j\C_k=sum_{i&j=k}A_i*B_j\C_k=sum_{ioplus j=k}A_i*B_j
]
这时就不能使用FFT
了
但是FFT
使我们产生了一种想法
我们能不能用一种类似FFT
的方法,用另一个多项式来表示(A,B),然后再对应相乘,最后再变换回来呢
答案是可以的,这就是FWT
,即快速沃尔什变换
咋搞啊
我们以或运算举例:
我们按照定义,显然可以构造 (FWT[A] = A' = sum_{i=i|j}A_{j}) ,来表示 (j) 满足二进制中 (1) 为 (i) 的子集。
那么显然会有 (C_{i} = sum_{i=j|k}A_{j}*B_{k} Rightarrow FWT[C] = FWT[A] * FWT[B])
至于上面这个是怎么来的:
[egin{aligned}
FWT[C][i]&=FWT[A][i]*FWT[B][i]\
sum_{j|i}C_j&=(sum_{j|i}A_j)*(sum_{j|i}B_j) \
sum_{j|i}C_j&=sum_{j|i,k|i} A_jB_k\
sum_{j|i}C_j&=sum_{j|i}sum_{a|b=j}A_aB_b\
C_j&=sum_{a|b=j}A_aB_b
end{aligned}
]
这样就和上面我们想要的式子一样了。
一堆定义/结论
别问我怎么推的,我也不知道。
在这里有详细的证明。
通用性质
性质1:
[FWT(Apm B)=FWT(A)pm FWT(B)
]
性质2:
定义(oplus)为任意集合运算
[FWT(Aoplus B)=FWT(A)*FWT(B) (对应相乘)
]
或运算
定义:
[FWT(A)[i]=sum_{j|i=i}A_j
]
正向运算:
[FWT(A)=egin{cases}(FWT(A_0),FWT(A_1)+FWT(A_0))& n>1\ A & n=0end{cases}
]
逆向运算:
[IFWT(A)=egin{cases}(IFWT(A_0),IFWT(A_1)-IFWT(A_0))&n>1\ A&n=0end{cases}
]
与运算
定义:
[FWT(A)[i]=sum_{i&j=j}A_i
]
正向运算:
[FWT(A)=egin{cases}(FWT(A_0)+FWT(A_1),FWT(A_1))&n>1\ A &n=0end{cases}
]
逆向运算:
[IFWT(A)=egin{cases}(IFWT(A_0)-IFWT(A_1),IFWT(A_1))&n>1\ A&n=0end{cases}
]
异或运算
定义:
令(d(x))为(x)在二进制下拥有的1的数量
[FWT(A)[i]=sum_{d(j&i)为偶数}A_j-sum_{d(k&i)为奇数}A_k
]
正向运算:
[FWT(A)=egin{cases}(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))&n>0\ A&n=0end{cases}
]
逆向运算:
[IFWT(A)=egin{cases}(frac{IFWT(A_0+A_1)}{2},frac{IFWT(A_0-A_1)}{2})&n>1\ A&n=0end{cases}
]
板子
- 按位或
- 按位与
- 按位异或
//by Harry_bh
void FWT1(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)a[j+(mid>>1)]+=a[j];
}
void IFWT1(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)a[j+(mid>>1)]-=a[j];
}
void FWT2(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)a[j]+=a[j+(mid>>1)];
}
void IFWT2(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)a[j]-=a[j+(mid>>1)];
}
void FWT3(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)
{
long long x=a[j],y=a[j+(mid>>1)];
a[j]=x+y,a[j+(mid>>1)]=x-y;
}
}
inline void IFWT3(long long a[],int len)
{
for(int mid=2;mid<=len;mid<<=1)
for(int i=0;i<len;i+=mid)
for(int j=i;j<i+(mid>>1);j++)
{
long long x=a[j],y=a[j+(mid>>1)];
a[j]=(x+y)>>1,a[j+(mid>>1)]=(x-y)>>1;
}
}