Preface
我们知道,对于下面的式子
f
(
n
)
=
∑
i
⊕
j
=
n
g
(
i
)
h
(
j
)
f(n)=\sum\limits_{i⊕j=n}g(i)h(j)
f(n)=i⊕j=n∑g(i)h(j)
当
⊕
⊕
⊕为+运算时,可以用FFT优化
当
⊕
⊕
⊕为位运算(or,and,xor)时是否也有一种优化呢
答案是肯定的。
FWT
首先,对于多项式,我们定义
A
∗
B
A*B
A∗B为某一种位运算卷积
显然它满足交换律、结合律
类似FFT。我们利用分治的思想,将多项式 A A A分成两部分 A 0 A_0 A0和 A 1 A_1 A1,表示其下标二进制最高位为0的部分(前 2 n − 1 2^{n-1} 2n−1项)和最高位为1的部分(后 2 n − 1 2^{n-1} 2n−1项),不足的不妨用0补齐
那么A是由
A
0
,
A
1
A_0,A_1
A0,A1两部分拼接而成,记为
A
=
(
A
0
,
A
1
)
A=(A_0,A_1)
A=(A0,A1)
对于一个多项式A,我们尝试找到一种变换
t
f
tf
tf
满足:
t
f
(
A
∗
B
)
=
t
f
(
A
)
t
f
(
B
)
tf(A*B)=tf(A)tf(B)
tf(A∗B)=tf(A)tf(B)
并且它存在逆变换,满足
u
t
f
(
t
f
(
A
)
)
=
A
utf(tf(A))=A
utf(tf(A))=A
这样对于两个多项式位运算卷积我们就可以先将它们变换,直接按项相乘,再变换回来
下面结论就来了
or卷积
当
⊕
⊕
⊕为or运算时
t
f
(
A
)
=
(
t
f
(
A
0
)
,
t
f
(
A
0
)
+
t
f
(
A
1
)
)
tf(A)=\left(tf(A_0),tf(A_0)+tf(A_1)\right)
tf(A)=(tf(A0),tf(A0)+tf(A1))
u
t
f
(
A
)
=
(
u
t
f
(
A
0
)
,
u
t
f
(
A
1
)
−
t
f
(
A
0
)
)
utf(A)=\left(utf(A_0),utf(A_1)-tf(A_0)\right)
utf(A)=(utf(A0),utf(A1)−tf(A0))
当然只有一个数时就是它本身
实际上,
t
f
(
A
)
[
i
]
=
∑
j
o
r
i
=
i
A
[
j
]
tf(A)[i]=\sum\limits_{j\ or\ i=i}A[j]
tf(A)[i]=j or i=i∑A[j]
and卷积
当
⊕
⊕
⊕为and运算时
t
f
(
A
)
=
(
t
f
(
A
0
)
+
t
f
(
A
1
)
,
t
f
(
A
1
)
)
tf(A)=\left(tf(A_0)+tf(A_1),tf(A_1)\right)
tf(A)=(tf(A0)+tf(A1),tf(A1))
u
t
f
(
A
)
=
(
u
t
f
(
A
0
)
−
u
t
f
(
A
1
)
,
u
t
f
(
A
1
)
)
utf(A)=\left(utf(A_0)-utf(A_1),utf(A_1)\right)
utf(A)=(utf(A0)−utf(A1),utf(A1))
xor 卷积
当
⊕
⊕
⊕为xor运算时
t
f
(
A
)
=
(
t
f
(
A
0
)
+
t
f
(
A
1
)
,
t
f
(
A
0
)
−
t
f
(
A
1
)
)
tf(A)=\left(tf(A_0)+tf(A_1),tf(A_0)-tf(A_1)\right)
tf(A)=(tf(A0)+tf(A1),tf(A0)−tf(A1))
u
t
f
(
A
)
=
(
u
t
f
(
A
0
)
+
u
t
f
(
A
1
)
2
,
u
t
f
(
A
0
)
−
u
t
f
(
A
1
)
2
)
utf(A)=\left({utf(A_0)+utf(A_1)\over 2},{utf(A_0)-utf(A_1)\over 2}\right)
utf(A)=(2utf(A0)+utf(A1),2utf(A0)−utf(A1))
并且对于所有的变换,都满足 t f ( A ) + t f ( B ) = t f ( A + B ) tf(A)+tf(B)=tf(A+B) tf(A)+tf(B)=tf(A+B)
证明可以用数学归纳法,拆成两部分推导一波…
博主太懒不想推了
接下来就可以快乐写FWT了
过程和FFT是类似的,不过不需要蝴蝶变换的反位
Code
or
void FWT(int *a,bool pd)
{
for(int m=2,half=1;m<=M;half=m,m<<=1)
for(int j=0;j<M;j+=m)
fo(i,0,half-1)
{
a[i+j+half]+=a[i+j]*((pd?)-1:1);
}
}
And
void FWT(int *a,bool pd)
{
for(int m=2,half=1;m<=M;half=m,m<<=1)
for(int j=0;j<M;j+=m)
fo(i,0,half-1)
{
a[i+j]+=a[i+j+half]*((pd?)-1:1);
}
}
Xor
void FWT(int *a,bool pd)
{
for(int m=2,half=1;m<=M;half=m,m<<=1)
for(int j=0;j<M;j+=m)
fo(i,0,half-1)
{
int v=a[i+j+half];
a[i+j+half]=a[i+j]-v,a[i+j]+=v;
if(pd) a[i+j]/=2,a[i+j+half]/=2;
}
}
一个应用(子集卷积)
有时候我们常常要碰到这样的问题
f
(
n
)
=
∑
i
o
r
j
=
n
,
i
a
n
d
j
=
0
g
(
i
)
h
(
j
)
f(n)=\sum\limits_{i\ or\ j=n,i\ and\ j=0}g(i)h(j)
f(n)=i or j=n,i and j=0∑g(i)h(j)
也就是子集卷补集
这似乎不太好处理
巧妙的转化一下
i
o
r
j
=
n
,
i
a
n
d
j
=
0
⟺
i
o
r
j
=
n
,
b
i
t
[
i
]
+
b
i
t
[
j
]
=
b
i
t
[
n
]
i\ or\ j=n,i\ and\ j=0 \Longleftrightarrow i\ or \ j=n,bit[i]+bit[j]=bit[n]
i or j=n,i and j=0⟺i or j=n,bit[i]+bit[j]=bit[n],其中
b
i
t
[
i
]
bit[i]
bit[i]为i在二进制下1的个数
如果我们将所有数按bit分组,每一组存一个多项式,只有bit数对应的项才有值,其他项为0
这就变成
F
b
i
t
[
n
]
(
x
)
=
∑
i
=
0
b
i
t
[
n
]
G
i
(
x
)
∗
H
b
i
t
[
n
]
−
i
(
x
)
F_{bit[n](x)}=\sum\limits_{i=0}^{bit[n]}G_i(x)*H_{bit[n]-i}(x)
Fbit[n](x)=i=0∑bit[n]Gi(x)∗Hbit[n]−i(x)
相当于里面的位运算卷积外面套一个普通的卷积。
枚举
b
i
t
[
n
]
bit[n]
bit[n],再枚举i,
t
f
(
G
)
tf(G)
tf(G)和
t
f
(
H
)
tf(H)
tf(H)可以提前预处理好,这里直接相乘
又由于
t
f
(
A
)
+
t
f
(
B
)
=
t
f
(
A
+
B
)
tf(A)+tf(B)=tf(A+B)
tf(A)+tf(B)=tf(A+B)。
因此我们可以直接累加进
t
f
(
F
b
i
t
[
n
]
)
tf(F_{bit[n]})
tf(Fbit[n]),最后全部做完了再一次性
u
t
f
utf
utf回来
注意不同的bit[n]之间的F是不能加在一起的,一个bit[n]做完以后只有bit对应的项的值是有意义的。
这样预处理和后面乘积累加时间复杂度都是 O ( n log 2 n ) 的 O(n\log ^2n)的 O(nlog2n)的,空间复杂度 O ( n log n ) O(n\log n) O(nlogn)