Fast Möbius Transform 学习笔记 | FMT
小 Tips:在计算机语言中 \(\cap\) =
&
/and
, \(\cup\) =|
/or
First. 定义
定义长度为 \(2^n\) 的序列的 and
卷积 \(A = B * C\) 为 \(A_i=\sum_{j \cap k = i}{B_j \times C_k}\)
考虑快速计算
Second. 变换
定义长度为 \(2^n\) 的序列的 Zeta 变换
为
\]
即子集和
它具有一下性质:
= \sum_{j \subseteq i} B_j \times \sum_{k \subseteq i} C_k
= \sum_{p \subseteq i} \sum_{j \cap k = p} B_j \times C_k
= \sum_{p \subseteq i} A_p = \hat A_i
\]
相当于 FFT
中的点值表示法,用以加速卷积过程
Third. 快速变换
暴力求解 \(\hat A\) 最优时间复杂度为 \(3^n\),考虑加速
考虑 子集DP
,有一篇很好的博客[1],这里大概讲解一下。
其实 \(\hat A\) 本质上是一个高维( \(n\) 维)前缀和
如果我们要求二维前缀和,显然可以通过一下代码实现:
// 1st
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
a[i][j]+=a[i-1][j];
// 2nd
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
a[i][j]+=a[i][j-1];
我们发现 1st
完成后 a[i][j]
表示的是 \(\sum_{k \le i} a_{k,j}\),即 \((i,j)\) 上方的元素和
那么 2nd
便是将 \((i,j)\) 左边操作后的 \(\sum_{k \le j} a_{i,k}\) 累加到 a[i][j]
中,即完成二维前缀和
下面拓展到三维前缀和
// 1st
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
for(int k=1; k<=n; ++k)
a[i][j][k]+=a[i-1][j][k];
// 2nd
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
for(int k=1; k<=n; ++k)
a[i][j][k]+=a[i][j-1][k];
// 3rd
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
for(int k=1; k<=n; ++k)
a[i][j][k]+=a[i][j][k-1];
类似地,1st,2nd
中处理除了第 k
平面中的二维前缀和,3rd
在立体空间中把他们加起来,成为三维前缀和
回到 Zeta 变换
:
\]
如果我们用二进制 \((x_{n-1}x_{n-2}x_{n-3}\ ...\ x_2x_1x_0)_2\) 表示集合 \(i\),二进制 \((y_{n-1}y_{n-2}y_{n-3}\ ...\ y_2y_1y_0)_2\) 表示集合 \(j\)
那么 \(\hat A_i\) 可以被视为 \(n\) 维前缀和,即
\]
当然,下标都为 0/1
因此,我们有如下 Code:
for(int i=0; i<n; ++i)
for(int j=0; j<(1<<n); ++j)
if(j&(1<<i))
a[j]+=a[j^(1<<i)];
以上 Code 的 naive 形式为:
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(i1>0) a[i1][i2][...][in]+=a[i1-1][i2][...][in];
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(i2>0) a[i1][i2][...][in]+=a[i1][i2-2][...][in];
...
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(in>0) a[i1][i2][...][in]+=a[i1][i2][...][in-1];
不要质疑我代码编译不通过
因此,我们用 \(O(n2^n)\) 的时间复杂度解决了 Zeta 变换
Fourth. 逆变换
warning:此处不是
莫比乌斯反演
!
众所周知,前缀和的逆运算即为差分
我们发现,只需要先将最后一维差分,即可将序列处理为 \(n-1\) 维前缀和
即
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(in>0) a[i1][i2][...][in]-=a[i1][i2][...][in-1];
...
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(i2>0) a[i1][i2][...][in]-=a[i1][i2-2][...][in];
for(int i1=0; i1<2; ++i1) for(int i2=0; i2<2; ++i2) ... for(int in=0; in<2; ++in) if(i1>0) a[i1][i2][...][in]-=a[i1-1][i2][...][in];
但实际上因为前缀和都是无序的,因此我们直接正着做就可以啦
只需要把正变换的 +=
改为 -=
就可以了
Fifth. 扩展
有时候,题目给的不是 \(\cap\) ,而是 \(\cup\) 怎么办?
那我们重定义 Zeta 变换
为:
\]
再推一下式子:
=\sum_{i\supseteq j} B_j \times \sum_{i\supseteq k} C_k
=\sum_{i\supseteq j,i\supseteq k} B_j \times C_k
=\sum_{i\supseteq p} \sum_{j \cup k=p} B_j \times C_k
=\sum_{i\supseteq p} A_p
=\hat A_i
\]
变换即超集和,高维后缀和
求法也很简单,只用将源代码中的 if(j&(1<<i))
该为 if(!(j&(1<<i)))
即可
是 \(\oplus\) 怎么办?
快去学 FWT 吧
Last. 例题
再送你一个并/交集的小口诀:下并或,上交与
Fast Möbius Transform 学习笔记 | FMT的更多相关文章
- ios 控件代码transform学习笔记
1.图片设置(平移,缩放,旋转) 创建一个transform属性 //按钮点击时,只能执行一次向上旋转 //派 M_PI_4 45度旋转 . CGAffineTransform transforms= ...
- css笔记 - transform学习笔记(二)
transform转换 CSS transform 属于2D/3D上的转换.变形效果.他不是一个动画,他就是变形.比如正方形变平行四边形,再变圆形.都是形状变成另一个形状. 但是如果配合上transi ...
- FMT/FWT学习笔记
目录 FMT/FWT学习笔记 FMT 快速莫比乌斯变换 OR卷积 AND卷积 快速沃尔什变换(FWT/XOR卷积) FMT/FWT学习笔记 FMT/FWT是算法竞赛中求or/and/xor卷积的算法, ...
- 「学习笔记」Fast Fourier Transform
前言 快速傅里叶变换(\(\text{Fast Fourier Transform,FFT}\) )是一种能在\(O(n \log n)\)的时间内完成多项式乘法的算法,在\(OI\)中的应用很多,是 ...
- [原创]java WEB学习笔记52:国际化 fmt 标签,国际化的总结
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)
目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...
- [学习笔记] 多项式与快速傅里叶变换(FFT)基础
引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积, 而代码量却非常小. 博主一年半前曾经因COGS的一 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十八章:立方体贴图
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十八章:立方体贴图 代码工程地址: https://github.c ...
- 快速傅里叶变换(FFT)学习笔记(其一)
再探快速傅里叶变换(FFT)学习笔记(其一) 目录 再探快速傅里叶变换(FFT)学习笔记(其一) 写在前面 为什么写这篇博客 一些约定 前置知识 多项式卷积 多项式的系数表达式和点值表达式 单位根及其 ...
- 【OI向】快速傅里叶变换(Fast Fourier Transform)
[OI向]快速傅里叶变换(Fast Fourier Transform) FFT的作用 在学习一项算法之前,我们总该关心这个算法究竟是为了干什么. (以下应用只针对OI) 一句话:求多项式 ...
随机推荐
- hadoop集群启动脚本文件myhadoop.sh
#!/bin/bash if [ $# -lt 1 ] then echo "No Args Input..." exit ; fi case $1 in "start& ...
- 【Java】归并排序
代码: 1 public static void mergeSort(int[] arr) { 2 if (arr == null || arr.length < 2) { 3 return; ...
- 冒泡排序【Java】
1 public class Paixu { 2 public static void main(String args[]) { 3 int myNum[] = {2,6,4,1,5}; 4 //从 ...
- ASCII编码的全面介绍
1. ASCII编码的定义和历史 ASCII(American Standard Code for Information Interchange)是一种用于将文本字符转换为数字编码的标准,最初由美国 ...
- 实际项目中如何使用Git做分支管理
前言 Git是一种强大的分布式版本控制系统,在实际项目开发中使用Git进行分支管理是非常常见的做法,因为它可以帮助团队高效的协作和管理项目的不同版本,今天我们来讲讲在实际项目中最常用的Git分支管理策 ...
- #最大公约数#CF346A Alice and Bob
题目传送门 CF346A 分析 可以发现其所能表示的数就是能被最大公约数整除的数,且这些数不能超过最大值, 于是判断一下取数的奇偶性即可 代码 #include <cstdio> #inc ...
- #树形dp,二次扫描换根法#JZOJ 3501 消息传递 with others
目录 JZOJ 3501 消息传递 题目 分析 代码 简单写就能跳过之题目 JZOJ 3500 物语 题目 分析 代码 JZOJ 3757 洛谷 2354 [NOI 2014] 随机数产生器 题目 分 ...
- 【原创】win11完美搭建macos13实现IOS自动化测试环境
虚拟机完美搭建IOS自动化测试环境 一.win系统 1.1 安装虚拟机VM16 如果是macos系统请跳过 详细安装请看压缩包,私信可以提供安装包 1.2 VM解锁macOS 使用的是unlocker ...
- 2024-04-13:用go语言,给定一个整数数组 `nums`, 请编写一个函数,返回一个新的数组 `counts`。 满足以下条件:对于每个 `nums[i]`, `counts[i]` 表示在
2024-04-13:用go语言,给定一个整数数组 nums, 请编写一个函数,返回一个新的数组 counts. 满足以下条件:对于每个 nums[i], counts[i] 表示在 nums[i] ...
- SVM三则
硬间隔SVM SVM被提出来, 解决模式识别中, 数据的分类问题,属于有监督算法中的一种, 如上图所示, 于其他的线性回归方式不同, SVM企图去寻找一个最完美的超平面, 因为能正确分类样本的线, 它 ...