前言
这好像是一个很巧妙的东西
我在FWT的博客里写到的FWTor和FWTand似乎其实并不是真正的FWT,好像就是FMT
而FWTxor才是真正的FWT
而这里有一个更为本质的FMT板子,分治算法可以去看我FWT的博客学习
另外,似乎这里的“莫比乌斯反演”和数论的“莫比乌斯反演有所不同”
本博客部分内容参考:吕凯风, 《集合幂级数的性质与应用及其快速算法》, 国家集训队2015论文集。
问题
现在已知两个函数
f
(
S
)
,
g
(
S
)
f(S),g(S)
f(S),g(S)
我们要求每个
F
(
S
)
=
∑
A
⊆
S
∑
B
⊆
S
f
(
A
)
g
(
B
)
F(S)=\sum_{A\subseteq S}\sum_{B\subseteq S}f(A)g(B)
F(S)=A⊆S∑B⊆S∑f(A)g(B)
解决
我们发现,我们把
f
f
f、
g
g
g、
F
F
F翻转后成为
f
′
f'
f′、
g
′
g'
g′、
F
′
F'
F′
可以列出以下式子:
F
′
(
S
)
=
∑
A
∩
B
=
S
f
′
(
A
)
g
′
(
B
)
F'(S)=\sum_{A\cap B=S}f'(A)g'(B)
F′(S)=A∩B=S∑f′(A)g′(B)
update2021/11/23:
翻转的含义是集合每个值的取/不取状态取反
式子应该为
F
′
(
S
)
=
∑
S
⊆
A
∩
B
f
′
(
A
)
g
′
(
B
)
F'(S)=\sum_{S\subseteq A\cap B}f'(A)g'(B)
F′(S)=S⊆A∩B∑f′(A)g′(B)
然后直接FWTand就好了
性质
由于在FWT的博客中我的证明是直接用定义+分治的
所以中间过程并没有很好的解释
我们来看and的FWT定义:
F
W
T
(
A
)
=
(
∑
i
&
0
=
0
a
i
,
∑
i
&
1
=
1
a
i
,
∑
i
&
2
=
2
a
i
⋅
⋅
⋅
∑
i
&
(
n
−
1
)
=
(
n
−
1
)
a
i
)
\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}
FWT(A)=(i&0=0∑ai,i&1=1∑ai,i&2=2∑ai⋅⋅⋅i&(n−1)=(n−1)∑ai)
我们发现把它当成集合,并且翻转之后
就变成了
F
′
(
S
)
=
∑
A
⊆
S
f
′
(
A
)
F'(S)=\sum_{A\subseteq S}f'(A)
F′(S)=A⊆S∑f′(A)
于是我们获得了很好的快速求这个式子的做法
设元素个数为
n
n
n,那么复杂度为
Θ
(
2
n
n
)
\Theta(2^nn)
Θ(2nn),比直接枚举子集的
Θ
(
3
n
)
\Theta(3^n)
Θ(3n)优了不少(应用例题:PKUWC2018随机游走)
其实这个东西有一种更加好写的写法、并且不需要翻转(其实真的只是压了一个循环)
贴出代码:
inline void FMT(int*A)
{
for(rg int i=1;i<1<<n;i<<=1)
for(rg int j=0;j<1<<n;j++)
if(i&j)
A[j]=(A[j]+A[j^i])%mod;
}
其实
f
(
S
)
=
∑
T
⊆
S
g
(
T
)
f(S)=\sum_{T\subseteq S}g(T)
f(S)=T⊆S∑g(T)
这个才是真正的快速莫比乌斯变换(FMT)
然后我们回到上面,IFWT()究竟是什么呢?
相当于就是进行反演
g
(
S
)
=
∑
T
⊆
S
(
−
1
)
∣
S
∣
−
∣
T
∣
f
(
T
)
g(S)=\sum_{T\subseteq S}(-1)^{|S|-|T|}f(T)
g(S)=T⊆S∑(−1)∣S∣−∣T∣f(T)
然后这就是快速莫比乌斯反演(FMI) 了
inline void FMI(int*A)
{
for(rg int i=1;i<1<<n;i<<=1)
for(rg int j=0;j<1<<n;j++)
if(i&j)
A[j]=A[j]-A[j^i];
}
整合一下变成
inline void FMT(int*A,const int fla)
{
for(rg int i=1;i<1<<n;i<<=1)
for(rg int j=0;j<1<<n;j++)
if(i&j)
A[j]=A[j]+fla*A[j^i];
}
总结
大概这是FWTor/FWTand的本质吧
这里的代码和之前的代码有所不同,但是本质一样,有兴趣可以自行推导一下
另外,根据测试,跑
2
25
2^{25}
225的数据的时候,这里的FMT花的时间大概是FWT里的写法跑的时间的1.2倍,略有点常数,所以还是推荐FWT里的and写法