本文主要内容是对武汉市第二中学吕凯风同学的论文《集合幂级数的性质与应用及其快速算法》的理解
定义
集合幂级数
为了更方便的研究集合的卷积,引入集合幂级数的概念
集合幂级数也是形式幂级数的一种,只是集合的一种表现形式,无需考虑收敛或发散的含义
定义一个集合 S S S 的集合幂级数为 f f f ,那么我们就可以把集合 S S S 表示为如下形式
f
=
∑
T
⊆
S
f
T
⋅
x
T
\begin{aligned}f=\sum _{T\subseteq S}f_{T}\cdot x^{T}\end{aligned}
f=T⊆S∑fT⋅xT
f
T
f_T
fT为
T
T
T这个集合幂级数的系数
简单来说就是用二进制表示集合的元素是否存在,并将其写成多项式的形式
约定
c
=
(
a
,
b
)
c=\left(a,b\right)
c=(a,b)表示将
a
,
b
a,b
a,b连起来组成
c
c
c
为了方便,将
f
∗
g
f*g
f∗g写成
f
g
fg
fg
卷积运算
加法
h
=
f
+
g
\begin{aligned}h=f+g\end{aligned}
h=f+g
那么
h
S
=
f
S
+
g
S
h_S=f_S+g_S
hS=fS+gS
减法
h
=
f
−
g
\begin{aligned}h=f-g\end{aligned}
h=f−g
那么
h
S
=
f
S
−
g
S
h_S=f_S-g_S
hS=fS−gS
乘法
h
=
f
∗
g
\begin{aligned}h=f*g\end{aligned}
h=f∗g
那么
h
S
=
∑
i
∘
j
⊆
S
f
i
×
g
j
\begin{aligned}h_S=\sum_{i∘j\subseteq S}f_i\times g_j\end{aligned}
hS=i∘j⊆S∑fi×gj
其中
∘
∘
∘可以是与,或,异或运算
集合并卷积 就是
∘
∘
∘进行或运算
子集卷积 就是
∘
∘
∘进行与运算
集合对称差卷积 就是
∘
∘
∘进行异或运算
快速求法
加法和减法都可以在
O
(
n
)
O(n)
O(n)时间复杂度内求出结果
对乘法,有一些优化的算法,以集合并卷积为例
分治
设
f
f
f有
2
n
2^n
2n项
考虑将其集合幂级数的第
n
n
n个元素提出来
则
f
=
f
−
+
x
{
n
}
f
+
f=f^-+x^{\{n\}}f^+
f=f−+x{n}f+,可以知道
f
−
f^-
f−为前
2
n
−
1
2^{n-1}
2n−1项,
f
+
f^+
f+为后
2
n
−
1
2^{n-1}
2n−1项即
f
−
f^-
f−的第
n
n
n个元素在二进制下为
0
0
0,
f
+
f^+
f+的第
n
n
n个元素在二进制下为
1
1
1
f
g
=
(
f
−
+
x
{
n
}
f
+
)
(
g
−
+
x
{
n
}
g
+
)
=
f
−
g
−
+
x
{
n
}
(
f
−
g
+
+
f
+
g
−
+
f
+
g
+
)
=
f
−
g
−
+
x
{
n
}
(
(
f
−
+
f
+
)
(
g
−
+
g
+
)
−
f
−
g
−
)
\begin{aligned}fg&=\left( f^-+x^{\{n\}}f^{+}\right) \left( g^{-}+x^{\{n\}}g^{+}\right)\\ &=f^-g^-+x^{\{n\}}\left( f^-g^{+}+f^{+}g^-+f^{+}g^{+}\right) \\ &=f^-g^-+x^{\{n\}}\left(\left(f^-+f^+\right)\left(g^-+g^+\right)-f^-g^-\right) \end{aligned}
fg=(f−+x{n}f+)(g−+x{n}g+)=f−g−+x{n}(f−g++f+g−+f+g+)=f−g−+x{n}((f−+f+)(g−+g+)−f−g−)
这样计算
f
g
fg
fg就只要计算
f
−
g
−
f^-g^-
f−g−和
(
f
−
+
f
+
)
(
g
−
+
g
+
)
\left(f^-+f^+\right)\left(g^-+g^+\right)
(f−+f+)(g−+g+)了
此时已经没有
n
n
n这个元素了
于是我们可以递归分治求解
f
g
fg
fg
时间复杂度
O
(
n
2
n
)
O\left(n2^n\right)
O(n2n)
C o d e \mathcal{Code} Code
void fold (int *f,int *g,int *h,int hlen)//hlen -> half len
{
if (hlen==1) return void(h[0]=f[0]*g[0]);
for (int i=0;i<hlen;++i) f[i+hlen]+=f[i],g[i+hlen]+=g[i];
fold(f,g,h,hlen>>1),fold(g+hlen,g+hlen,h+hlen,hlen>>1);
for (int i=0;i<hlen;++i) f[i+hlen]-=f[i],g[i+hlen]-=g[i];
}
快速莫比乌斯变换(FMT)
对于一个集合幂级数
f
f
f,我们定义其快速莫比乌斯变换为集合幂级数
f
^
\widehat f
f
,使其系数满足
f
^
S
=
∑
T
⊆
S
f
T
\begin{aligned}\widehat f_S=\sum_{T\subseteq S}f_{T}\end{aligned}
f
S=T⊆S∑fT
由容斥原理,我们可以得到
f
S
=
∑
T
⊆
S
(
−
1
)
∣
S
∣
−
∣
T
∣
f
^
T
\begin{aligned}f_S=\sum_{T\subseteq S}\left(-1\right)^{|S|-|T|}\widehat f_T\end{aligned}
fS=T⊆S∑(−1)∣S∣−∣T∣f
T
考虑乘法
h
^
=
f
^
g
^
\widehat h=\widehat f\widehat g
h
=f
g
h
^
s
=
∑
i
⊆
S
∑
j
⊆
S
f
i
g
j
=
(
∑
i
⊆
S
f
i
)
(
∑
j
⊆
S
g
j
)
=
f
^
S
g
^
S
\begin{aligned}\widehat h_{s}&=\sum _{i\subseteq S}\sum _{j\subseteq S}f_{i}g_{j}\\ &=\left(\sum _{i\subseteq S}f_i\right)\left(\sum _{j\subseteq S}g_{j}\right)\\ &=\widehat f_S \widehat g_S \end{aligned}
h
s=i⊆S∑j⊆S∑figj=(i⊆S∑fi)⎝⎛j⊆S∑gj⎠⎞=f
Sg
S
那么,现在我们知道想办法怎么求
f
^
\widehat f
f
和
g
^
\widehat g
g
,然后把它们的系数乘起来,就可以得到
h
^
\widehat h
h
然后再将其反演得到
f
f
f(因为容斥是肯定会超时的)
考虑递推
我们设
f
^
S
(
i
)
\widehat f_S^{\left(i\right)}
f
S(i)使其满足
f
^
S
(
i
)
=
∑
T
⊆
S
[
(
S
−
T
)
⊆
{
1
,
2
,
…
,
i
}
]
f
T
\begin{aligned}\widehat f_S^{\left(i\right)}=\sum _{T\subseteq S}\left[ \left( S-T\right) \subseteq \left\{ 1,2,\ldots ,i\right\} \right] f_{T}\end{aligned}
f
S(i)=T⊆S∑[(S−T)⊆{1,2,…,i}]fT
即若
i
+
1
∼
n
i+1\sim n
i+1∼n有元素属于
S
S
S,则必须要选择,而
1
∼
i
1\sim i
1∼i中的元素可有可无
那么我们最终的
f
^
S
=
f
^
S
(
n
)
\widehat f_S=\widehat f_S^{\left(n\right)}
f
S=f
S(n),所有的元素都是可有可无的,即它的子集都被包含在内了
考虑第
S
S
S中有没有
i
i
i这个元素
- 没有,则 f ^ S ( i ) = f ^ S ( i − 1 ) \widehat f_S^{\left(i\right)}=\widehat f_S^{\left(i-1\right)} f S(i)=f S(i−1)
- 有,那么 f ^ S ( i ) = f ^ S ( i − 1 ) + f ^ S − i ( i − 1 ) \widehat f^{\left( i\right) }_{S}=\widehat f^{\left( i-1\right) }_{S}+\widehat f^{\left( i-1\right) }_{S- i} f S(i)=f S(i−1)+f S−i(i−1), S − i S-i S−i表示从 S S S这个集合中去掉 i i i这个元素,这个式子的后两项前者是第 i i i个元素一定被选了,后者则是第 i i i个元素一定没有被选
而要将其反演,我们考虑其逆过程,只需将所有加上来的全部减去即可
时间复杂度 O ( n 2 n ) O\left(n2^n\right) O(n2n)
C o d e \mathcal{Code} Code
上面做法都是二维数组
考虑先枚举
i
i
i再算所有
S
S
S的答案,只需一维数组即可
void FMT (int *a,int n)//n个元素
{
int all=1<<n;
for (int i=0;i<n;++i)
for (int j=0;j<all;++j)
if (j>>i&1) a[j]+=a[j^(1<<i)];
}
void IFMT (int *a,int n)//n个元素
{
int all=1<<n;
for (int i=0;i<n;++i)
for (int j=0;j<all;++j)
if (j>>i&1) a[j]-=a[j^(1<<i)];
}
快速沃尔什变换(FWT)
我们发现,在进行分治算法中,只需保留出
f
−
,
g
−
f^-,g^-
f−,g−,
(
f
−
+
f
+
)
,
(
g
−
+
g
+
)
\left(f^-+f^+\right),\left(g^-+g^+\right)
(f−+f+),(g−+g+)就可以算出答案了
可惜递归的常数相对来说太大,我们考虑将其写成循环的形式就可以得到
F
W
T
FWT
FWT了
若不考虑分治写成循环,我们换一种方法理解
F
W
T
FWT
FWT,当然,这是另一种思路了,上面将递归改成循环的思路是正确的
上面的
f
=
f
−
+
f
+
f=f^-+f^+
f=f−+f+,我们是始终让其满足这个条件的,所以在后面还减去了一个
f
−
g
−
f^-g^-
f−g−
让我们跳出思维的局限,弄这么一个
f
′
,
g
′
f',g'
f′,g′使其满足
(
f
g
)
′
=
f
′
g
′
(fg)'=f'g'
(fg)′=f′g′,这样我们只要计算
f
′
g
′
f'g'
f′g′,然后把它反演一下就可以得到
f
g
fg
fg
这里呢
f
′
=
(
f
−
,
f
−
+
f
+
)
f'=\left(f^-,f^-+f^+\right)
f′=(f−,f−+f+)
为什么这样就可以呢
考虑
(
f
g
)
′
(fg)'
(fg)′,根据上面的推导
(
f
g
)
′
=
(
f
−
g
−
,
(
f
−
+
f
+
)
(
g
−
+
g
+
)
)
\left(fg\right)'=\left(f^-g^-,\left(f^-+f^+\right)\left(g^-+g^+\right)\right)
(fg)′=(f−g−,(f−+f+)(g−+g+))
再考虑
f
′
g
′
f'g'
f′g′
f
′
−
g
′
−
=
f
−
g
−
f'^-g'^-=f^-g^-
f′−g′−=f−g−
f
′
+
g
′
+
=
(
f
−
+
f
+
)
(
g
−
+
g
+
)
f'^+g'^+=\left(f^-+f^+\right)\left(g^-+g^+\right)
f′+g′+=(f−+f+)(g−+g+)
所以这样是可以的
于是我们可以得到
f
′
=
(
f
−
,
f
−
+
f
+
)
f'=\left(f^-,f^-+f^+\right)
f′=(f−,f−+f+)
然后称这样的
f
′
f'
f′叫做沃尔什变换
F
W
T
(
f
)
=
F
W
T
(
f
−
,
f
−
+
f
+
)
FWT\left(f\right)=FWT\left(f^-,f^-+f^+\right)
FWT(f)=FWT(f−,f−+f+)
反演也很简单
即将多算的
f
−
f^-
f−减去即可
C o d e \mathcal{Code} Code
void FWT (int *a,int n)
{
for (int len=2;len<=n;len<<=1)
for (int i=0,hlen=len>>1;i<n;i+=len)
for (int j=i,k=j+hlen;j<k;++j)
a[j+hlen]+=a[j];
}
void IFWT (int *a,int n)
{
for (int len=2;len<=n;len<<=1)
for (int i=0,hlen=len>>1;i<n;i+=len)
for (int j=i,k=j+hlen;j<k;++j)
a[j+hlen]-=a[j];
}
时间复杂度 O ( n 2 n ) O\left(n2^n\right) O(n2n)
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧
如能得到推荐博主就更开心了
您的鼓励是博主的动力