也许更好的阅读体验

本文主要内容是对武汉市第二中学吕凯风同学的论文《集合幂级数的性质与应用及其快速算法》的理解

定义

集合幂级数

为了更方便的研究集合的卷积,引入集合幂级数的概念

集合幂级数也是形式幂级数的一种,只是集合的一种表现形式,无需考虑收敛或发散的含义

定义一个集合 \(S\) 的集合幂级数为 \(f\) ,那么我们就可以把集合 \(S\) 表示为如下形式

\(\begin{aligned}f=\sum _{T\subseteq S}f_{T}\cdot x^{T}\end{aligned}\)

\(f_T\)为\(T\)这个集合幂级数的系数

简单来说就是用二进制表示集合的元素是否存在,并将其写成多项式的形式

约定

\(c=\left(a,b\right)\)表示将\(a,b\)连起来组成\(c\)

为了方便,将\(f*g\)写成\(fg\)

卷积运算

加法

\(\begin{aligned}h=f+g\end{aligned}\)

那么

\(h_S=f_S+g_S\)

减法

\(\begin{aligned}h=f-g\end{aligned}\)

那么

\(h_S=f_S-g_S\)

乘法

\(\begin{aligned}h=f*g\end{aligned}\)

那么

\(\begin{aligned}h_S=\sum_{i∘j\subseteq S}f_i\times g_j\end{aligned}\)

其中\(∘\)可以是与,或,异或运算

集合并卷积 就是\(∘\)进行或运算

子集卷积 就是\(∘\)进行与运算

集合对称差卷积 就是\(∘\)进行异或运算


快速求法

加法和减法都可以在\(O(n)\)时间复杂度内求出结果

对乘法,有一些优化的算法,以集合并卷积为例

分治

设\(f\)有\(2^n\)项

考虑将其集合幂级数的第\(n\)个元素提出来

则\(f=f^-+x^{\{n\}}f^+\),可以知道\(f^-\)为前\(2^{n-1}\)项,\(f^+\)为后\(2^{n-1}\)项即\(f^-\)的第\(n\)个元素在二进制下为\(0\),\(f^+\)的第\(n\)个元素在二进制下为\(1\)

\(\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^-g^-\)和\(\left(f^-+f^+\right)\left(g^-+g^+\right)\)了

此时已经没有\(n\)这个元素了

于是我们可以递归分治求解\(fg\)

时间复杂度\(O\left(n2^n\right)\)

\(\mathcal{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\),我们定义其快速莫比乌斯变换为集合幂级数\(\widehat f\),使其系数满足

\(\begin{aligned}\widehat f_S=\sum_{T\subseteq S}f_{T}\end{aligned}\)

由容斥原理,我们可以得到

\(\begin{aligned}f_S=\sum_{T\subseteq S}\left(-1\right)^{|S|-|T|}\widehat f_T\end{aligned}\)

考虑乘法\(\widehat h=\widehat f\widehat g\)

\(\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}\)

那么,现在我们知道想办法怎么求\(\widehat f\)和\(\widehat g\),然后把它们的系数乘起来,就可以得到\(\widehat h\)

然后再将其反演得到\(f\)(因为容斥是肯定会超时的)

考虑递推

我们设\(\widehat f_S^{\left(i\right)}\)使其满足

\(\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}\)

即若\(i+1\sim n\)有元素属于\(S\),则必须要选择,而\(1\sim i\)中的元素可有可无

那么我们最终的\(\widehat f_S=\widehat f_S^{\left(n\right)}\),所有的元素都是可有可无的,即它的子集都被包含在内了

考虑第\(S\)中有没有\(i\)这个元素

  • 没有,则\(\widehat f_S^{\left(i\right)}=\widehat f_S^{\left(i-1\right)}\)
  • 有,那么\(\widehat f^{\left( i\right) }_{S}=\widehat f^{\left( i-1\right) }_{S}+\widehat f^{\left( i-1\right) }_{S- i}\),\(S-i\)表示从\(S\)这个集合中去掉\(i\)这个元素,这个式子的后两项前者是第\(i\)个元素一定被选了,后者则是第\(i\)个元素一定没有被选

而要将其反演,我们考虑其逆过程,只需将所有加上来的全部减去即可

时间复杂度\(O\left(n2^n\right)\)

\(\mathcal{Code}\)

上面做法都是二维数组

考虑先枚举\(i\)再算所有\(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^-\),\(\left(f^-+f^+\right),\left(g^-+g^+\right)\)就可以算出答案了

可惜递归的常数相对来说太大,我们考虑将其写成循环的形式就可以得到\(FWT\)了

若不考虑分治写成循环,我们换一种方法理解\(FWT\),当然,这是另一种思路了,上面将递归改成循环的思路是正确的

上面的\(f=f^-+f^+\),我们是始终让其满足这个条件的,所以在后面还减去了一个\(f^-g^-\)

让我们跳出思维的局限,弄这么一个\(f',g'\)使其满足\((fg)'=f'g'\),这样我们只要计算\(f'g'\),然后把它反演一下就可以得到\(fg\)

这里呢

\(f'=\left(f^-,f^-+f^+\right)\)

为什么这样就可以呢

考虑\((fg)'\),根据上面的推导

\(\left(fg\right)'=\left(f^-g^-,\left(f^-+f^+\right)\left(g^-+g^+\right)\right)\)

再考虑\(f'g'\)

\(f'^-g'^-=f^-g^-\)

\(f'^+g'^+=\left(f^-+f^+\right)\left(g^-+g^+\right)\)

所以这样是可以的

于是我们可以得到

\(f'=\left(f^-,f^-+f^+\right)\)

然后称这样的\(f'\)叫做沃尔什变换

\(FWT\left(f\right)=FWT\left(f^-,f^-+f^+\right)\)

反演也很简单

即将多算的\(f^-\)减去即可

\(\mathcal{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\left(n2^n\right)\)

如有哪里讲得不是很明白或是有错误,欢迎指正

如您喜欢的话不妨点个赞收藏一下吧

如能得到推荐博主就更开心了

您的鼓励是博主的动力

集合并卷积的三种求法(分治乘法,快速莫比乌斯变换(FMT),快速沃尔什变换(FWT))的更多相关文章

  1. 卷积的三种模式:full, same, valid

    通常用外部api进行卷积的时候,会面临mode选择. 本文清晰展示三种模式的不同之处,其实这三种不同模式是对卷积核移动范围的不同限制. 设 image的大小是7x7,filter的大小是3x3 1,f ...

  2. 卷积的三种模式:full、same、valid + 卷积输出size的计算

    转自https://blog.csdn.net/u012370185/article/details/95238828 通常用外部api进行卷积的时候,会面临mode选择. 这三种mode的不同点:对 ...

  3. java 集合之HashMap的三种遍历

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. 这周我们只需记住三种遍历方法 1.通过keySet()获取键,再利用hashmap里面的.get(key)方法通过键获取 ...

  4. [OI笔记]三种逆元的求法

    其实这篇博客只是搬运一下我之前(大概是NOIP那会)写在word里的笔记- 下面直接复制原话,题目是洛谷上求逆元的模板题:https://www.luogu.org/problemnew/show/P ...

  5. 【Java EE 学习 28 上】【oracle学习第二天】【子查询】【集合运算】【几种数据库对象】

    一.子查询 1.为什么要使用子查询:问题不能一步求解或者一个查询不能通过一步查询得到. 2.分类:单行子查询和多行子查询. 3.子查询的本质:一个查询中包含了另外一个或者多个查询. 4.使用子查询的规 ...

  6. LCS的几种求法

    \(LCS:\) 对于两个长度均为 \(N\) 的数列 \(A\) 和 \(B\) ,存在一个数列 \(C\) 使得 \(C\) 既是 \(A\) 的子序列有事 \(B\) 的子序列,现在需要求这个数 ...

  7. Java使用三种不同循环结构对1+2+3+...+100 求和

    ▷//第一种求法,使用while结构 /** * @author 9527 * @since 19/6/20 */ public class Gaosi { public static void ma ...

  8. UOJ272 [清华集训2016] 石家庄的工人阶级队伍比较坚强 【分治乘法】

    题目分析: 首先不难注意到式子就是异或卷积,所以考虑用分治乘法推出优化方法.我们把一个整体$f$拆成$f-,f\pm,f+$,然后另一个拆成$g-,g\pm,g+$.这样做的好处是能更清楚的分析问题. ...

  9. java中遍历集合的三种方式

    第一种遍历集合的方式:将集合变为数组 package com.lw.List; import java.util.ArrayList; import java.util.List; import ja ...

随机推荐

  1. 信息论 | information theory | 信息度量 | information measures | R代码(一)

    这个时代已经是多学科相互渗透的时代,纯粹的传统学科在没落,新兴的交叉学科在不断兴起. life science neurosciences statistics computer science in ...

  2. Linux 服务器远程管理

    一.Linux 常用远程管理工具 点击下载 二.查看服务器 ip 地址命令 1.通过 ip addr 查看网卡 ip 地址 ip addr 2.通过 ifconfig 查看网卡 ip 地址 最小化安装 ...

  3. Django之Restful API

    理解Restful架构:http://www.ruanyifeng.com/blog/2011/09/restful RESTful设计指南:http://www.ruanyifeng.com/blo ...

  4. ABAP函数篇1 日期函数

    1. 日期格式字段检查 data:l_date type ekko-bedat. l_date = '20080901'. CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY ...

  5. Python - Django - ORM Django 终端打印 SQL 语句

    在 settings.py 中添加以下内容: LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'c ...

  6. Python第一阶段06

    1.面向对象编程: class Dog: def __init__(self, name): self.name = name def bulk(self): print("%s 汪汪汪.. ...

  7. 【pod无法删除 总是处于terminate状态】强行删除pod

    加参数 --force --grace-period=0,grace-period表示过渡存活期,默认30s,在删除POD之前允许POD慢慢终止其上的容器进程,从而优雅退出,0表示立即终止POD ku ...

  8. DataTable.NET 使用server-side processing

    https://datatables.net/examples/server_side/simple.html 當頁面上要顯示的數據在10萬筆以上時,可以使用server-side processin ...

  9. Pyhon时间参数的应用

    Python获取 本周,上周,本月,上月,本季,上季,今年, 去年 # -*- coding: utf-8 -*-# @time: 2019-05-13 17:30 import datetime f ...

  10. 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识1

    什么是REST REST 是 Representational State Transfer 的缩写. 它是一种架构的风格, 这种风格基于一套预定义的规则, 这些规则描述了网络资源是如何定义和寻址的. ...