所谓快速沃尔什变换,就是快速的沃尔玛什锦专柜变换
(逃)
前言
正常卷积的定义:
c
k
=
∑
i
+
j
=
k
a
i
b
j
c_k=\sum_{i+j=k}a_ib_j
ck=∑i+j=kaibj。
可以用FFT或者NTT在
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的复杂度内解决。
然而,有些时候,我们计算卷积的时候下标关系并不是喜闻乐见的加法,而是形如
c
k
=
∑
i
⊕
j
=
k
a
i
b
j
c_k=\sum_{i\oplus j=k}a_ib_j
ck=∑i⊕j=kaibj,其中的
⊕
\oplus
⊕ 是 and or xor
中的一种位运算。
这个时候就需要快速莫比乌斯变换(and or
)和快速沃尔什变换(xor
),同样可以把复杂度降到
O
(
n
log
n
)
O(n\log n)
O(nlogn) 级别。
解析
由于三个运算思想比较类似,因此不对两个算法进行特别的区分,统称为FWT。
采用类比的思想,FFT先把多项式转化为点值表示,乘到一起,最后再反演回去。
类似的,FWT也是先把多项式
A
,
B
A,B
A,B 转化为某种变换
FWT
(
A
)
,
FWT
(
B
)
\operatorname{FWT}(A),\operatorname{FWT}(B)
FWT(A),FWT(B),然后乘起来得到
FWT
(
C
)
\operatorname{FWT}(C)
FWT(C),最后再反演回去得到
C
C
C。
根据不同位运算的性质,对应的变换有所不同。
OR
定义
求:
c
k
=
∑
i
∣
j
=
k
a
i
b
j
c_k=\sum_{i\operatorname{|}j=k}a_ib_j
ck=∑i∣j=kaibj
考虑或运算有如下性质:若
i
∣
k
=
k
,
j
∣
k
=
k
i|k=k,j|k=k
i∣k=k,j∣k=k,那么
(
i
∣
j
)
∣
k
=
k
(i|j)|k=k
(i∣j)∣k=k,反之亦然。
那么我们就按照这个充要条件设计变换:
FWT
o
r
(
A
)
k
=
∑
i
∣
k
=
k
A
i
\operatorname{FWT}_{or}(A)_k=\sum_{i|k=k}A_i
FWTor(A)k=∑i∣k=kAi。
自然语言:
i
i
i 必须是
k
k
k 的子集才能转移。
考虑两个变换后的多项式逐项系数相乘:
FWT
o
r
(
A
)
∗
FWT
o
r
(
B
)
=
∑
k
(
∑
i
∣
k
=
k
a
i
)
×
(
∑
j
∣
k
=
k
b
j
)
\operatorname{FWT}_{or}(A)*\operatorname{FWT}_{or}(B)=\sum_{k}(\sum_{i|k=k}a_i)\times (\sum_{j|k=k}b_j)
FWTor(A)∗FWTor(B)=k∑(i∣k=k∑ai)×(j∣k=k∑bj)
=
∑
k
∑
i
∣
k
=
k
∑
j
∣
k
=
k
a
i
b
j
=
∑
k
∑
(
i
∣
j
)
∣
k
=
k
a
i
b
j
=
FWT
o
r
(
C
)
=\sum_{k}\sum_{i|k=k}\sum_{j|k=k}a_ib_j=\sum_{k}\sum_{(i|j)|k=k}a_ib_j=\operatorname{FWT}_{or}(C)
=k∑i∣k=k∑j∣k=k∑aibj=k∑(i∣j)∣k=k∑aibj=FWTor(C)
所以我们就证出了系数直接相乘是对的,现在只需要一种快速的变换得到
FWT
o
r
\operatorname{FWT}_{or}
FWTor 以及将其反演的方法。
变换:
还是和FFT类似的,考虑分治,假设我们要合并两个长度为
2
n
2^n
2n 的序列,设它们不同的最高位为1的序列为
A
1
A_1
A1,为0的为
A
0
A_0
A0。
考虑 or
的性质,
A
0
A_0
A0 可以转移到
A
1
A_1
A1,而
A
1
A_1
A1 无法转移到
A
0
A_0
A0,因此有:
FWT
o
r
(
A
)
=
(
FWT
o
r
(
A
0
)
,
FWT
o
r
(
A
0
)
+
FWT
o
r
(
A
1
)
)
\operatorname{FWT}_{or}(A)=(\operatorname{FWT}_{or}(A_0),\operatorname{FWT}_{or}(A_0)+\operatorname{FWT}_{or}(A_1))
FWTor(A)=(FWTor(A0),FWTor(A0)+FWTor(A1))
其中
+
+
+ 表示系数逐项相加,
(
f
(
x
)
,
g
(
x
)
)
(f(x),g(x))
(f(x),g(x)) 表示把
f
(
x
)
f(x)
f(x) 和
g
(
x
)
g(x)
g(x) 顺次写出。
逆变换
那么对应的,还原成原序列只需要逆向操作即可:
UFWT
o
r
(
A
)
=
(
UFWT
o
r
(
A
0
)
,
UFWT
o
r
(
A
0
)
−
UFWT
o
r
(
A
1
)
)
\operatorname{UFWT}_{or}(A)=(\operatorname{UFWT}_{or}(A_0),\operatorname{UFWT}_{or}(A_0)-\operatorname{UFWT}_{or}(A_1))
UFWTor(A)=(UFWTor(A0),UFWTor(A0)−UFWTor(A1))
代码
写起来非常简洁:
void Or(ll *x,int n,int op){
for(int l=1;l<n;l<<=1){
for(int st=0;st<n;st+=l*2){
for(int i=0;i<l;i++){
ll u=x[st+i],v=x[st+l+i];
if(op==1) x[st+i]=u,x[st+l+i]=(v+u)%mod;
else x[st+i]=u,x[st+l+i]=(v+mod-u)%mod;
}
}
}
}
AND
和 or
的整个定义、证明几乎都是一样的。
求:
c
k
=
∑
i
&
j
=
k
a
i
b
j
c_k=\sum_{i\operatorname{\&}j=k}a_ib_j
ck=∑i&j=kaibj
与运算有如下性质:若
i
&
k
=
k
,
j
&
k
=
k
i\&k=k,j\&k=k
i&k=k,j&k=k,那么
(
i
&
j
)
&
k
=
k
(i\&j)\&k=k
(i&j)&k=k,反之亦然。
按照这个充要条件设计变换:
FWT
o
r
(
A
)
k
=
∑
i
∣
k
=
k
A
i
\operatorname{FWT}_{or}(A)_k=\sum_{i|k=k}A_i
FWTor(A)k=∑i∣k=kAi。
自然语言:
i
i
i 必须是
k
k
k 的超集才能转移。
后面的证明一模一样,变换也可以同理的得到:
FWT
a
n
d
(
A
)
=
(
FWT
a
n
d
(
A
0
)
+
FWT
a
n
d
(
A
1
)
,
FWT
a
n
d
(
A
1
)
)
\operatorname{FWT}_{and}(A)=(\operatorname{FWT}_{and}(A_0)+\operatorname{FWT}_{and}(A_1),\operatorname{FWT}_{and}(A_1))
FWTand(A)=(FWTand(A0)+FWTand(A1),FWTand(A1))
UFWT
a
n
d
(
A
)
=
(
UFWT
a
n
d
(
A
0
)
−
UFWT
a
n
d
(
A
1
)
,
UFWT
a
n
d
(
A
1
)
)
\operatorname{UFWT}_{and}(A)=(\operatorname{UFWT}_{and}(A_0)-\operatorname{UFWT}_{and}(A_1),\operatorname{UFWT}_{and}(A_1))
UFWTand(A)=(UFWTand(A0)−UFWTand(A1),UFWTand(A1))
代码
void And(ll *x,int n,int op){
for(int l=1;l<n;l<<=1){
for(int st=0;st<n;st+=l*2){
for(int i=0;i<l;i++){
ll u=x[st+i],v=x[st+l+i];
if(op==1) x[st+i]=(u+v)%mod,x[st+l+i]=v;
else x[st+i]=(u+mod-v)%mod,x[st+l+i]=v;
}
}
}
}
XOR
这个和之前的有所不同。
定义
求:
c
k
=
∑
i
⊕
j
=
k
a
i
b
j
c_k=\sum_{i\operatorname{\oplus}j=k}a_ib_j
ck=∑i⊕j=kaibj
设
d
(
x
)
d(x)
d(x) 为二进制下
x
x
x 的1的个数的奇偶性(模2的结果)。
有性质:
d
(
i
&
k
)
⊕
d
(
j
&
k
)
=
d
(
(
i
⊕
j
)
&
k
)
d(i\& k)\oplus d(j\& k)=d((i\oplus j)\& k)
d(i&k)⊕d(j&k)=d((i⊕j)&k)。
证明:
由于与运算,只考虑
k
k
k 为1的那些位。如果某一位
i
,
j
i,j
i,j 都是1或0,那么等号两边的奇偶性都不改变;如果某一位
i
,
j
i,j
i,j 一个是1一个是0,那么等号两边的奇偶性都改变;所以等号两遍的奇偶性始终同步改变,最后就一定是相等的。
设计:
FWT
x
o
r
(
A
)
k
=
∑
i
(
−
1
)
d
(
i
&
k
)
a
i
\operatorname{FWT}_{xor}(A)_k=\sum_{i} (-1)^{d(i\& k)}a_i
FWTxor(A)k=i∑(−1)d(i&k)ai
那么还是尝试逐项相乘:
FWT
x
o
r
(
A
)
∗
FWT
x
o
r
(
B
)
=
∑
k
(
∑
i
(
−
1
)
d
(
i
&
k
)
a
i
)
×
(
∑
j
(
−
1
)
d
(
j
&
k
)
b
j
)
\operatorname{FWT}_{xor}(A)*\operatorname{FWT}_{xor}(B)=\sum_k(\sum_{i} (-1)^{d(i\& k)}a_i)\times (\sum_{j} (-1)^{d(j\& k)}b_j)
FWTxor(A)∗FWTxor(B)=k∑(i∑(−1)d(i&k)ai)×(j∑(−1)d(j&k)bj)
=
∑
k
∑
i
,
j
(
−
1
)
d
(
i
&
k
)
+
d
(
j
&
k
)
a
i
b
j
=
∑
k
∑
i
,
j
(
−
1
)
d
(
i
&
k
)
⊕
d
(
j
&
k
)
a
i
b
j
=\sum_k\sum_{i,j} (-1)^{d(i\& k)+d(j\& k)}a_ib_j=\sum_k\sum_{i,j} (-1)^{d(i\& k)\oplus d(j\& k)}a_ib_j
=k∑i,j∑(−1)d(i&k)+d(j&k)aibj=k∑i,j∑(−1)d(i&k)⊕d(j&k)aibj
=
∑
k
∑
i
,
j
(
−
1
)
d
(
(
i
⊕
j
)
&
k
)
a
i
b
j
=
FWT
x
o
r
(
C
)
=\sum_k\sum_{i,j} (-1)^{d((i\oplus j)\& k)}a_ib_j=\operatorname{FWT}_{xor}(C)
=k∑i,j∑(−1)d((i⊕j)&k)aibj=FWTxor(C)
得证。
变换
还是分治的思想,考虑到增加一位后,只有下标最高位带1的数贡献到了下标最高位带1的位置时,最高位与运算结果为1,1的个数增加一个,奇偶性改变,所有的符号都变为相反。其它的转移都不变。
也就是:
FWT
x
o
r
(
A
)
=
(
FWT
x
o
r
(
A
0
)
+
FWT
x
o
r
(
A
1
)
,
FWT
x
o
r
(
A
0
)
−
FWT
x
o
r
(
A
1
)
)
\operatorname{FWT}_{xor}(A)=(\operatorname{FWT}_{xor}(A_0)+\operatorname{FWT}_{xor}(A_1),\operatorname{FWT}_{xor}(A_0)-\operatorname{FWT}_{xor}(A_1))
FWTxor(A)=(FWTxor(A0)+FWTxor(A1),FWTxor(A0)−FWTxor(A1))
逆变换
反过来即可。
FWT
x
o
r
(
A
)
=
(
FWT
x
o
r
(
A
0
)
+
FWT
x
o
r
(
A
1
)
2
,
FWT
x
o
r
(
A
0
)
−
FWT
x
o
r
(
A
1
)
2
)
\operatorname{FWT}_{xor}(A)=(\frac{\operatorname{FWT}_{xor}(A_0)+\operatorname{FWT}_{xor}(A_1)}{2},\frac{\operatorname{FWT}_{xor}(A_0)-\operatorname{FWT}_{xor}(A_1)}{2})
FWTxor(A)=(2FWTxor(A0)+FWTxor(A1),2FWTxor(A0)−FWTxor(A1))
代码
void Xor(ll *x,int n,int op){
for(int l=1;l<n;l<<=1){
for(int st=0;st<n;st+=l*2){
for(int i=0;i<l;i++){
ll u=x[st+i],v=x[st+l+i];
if(op==1) x[st+i]=(u+v)%mod,x[st+l+i]=(u+mod-v)%mod;
else x[st+i]=(u+v)%mod*499122177%mod,x[st+l+i]=(u+mod-v)%mod*499122177%mod;
}
}
}
}
Thanks for reading!