它们两个的全名叫 快速沃尔什变换(FWT) 和 快速莫比乌斯变换(FMT),用来在 \(O(n\log n)\) 时间复杂度内求位运算卷积。

因为 FMT 能解决的问题是 FWT 的子集,所以这里不讲 FMT,把它拎出来是想说它们两个的区别。

参考资料:偶耶XJX-浅谈快速沃尔什变换(FWT)&快速莫比乌斯变换(FMT)zcxxn-多项式学习笔记

位运算卷积

位运算卷积,就是求下面这个问题:

给定长度为 \(n\) 的序列 \(A,B\),求它们的卷积 \(C_i=\sum\limits_{j\oplus k=i} A_j\times B_k\),其中 \(\oplus\) 为 \(\text{and,or,xor}\) 中的一种。这里如果把 \(\oplus\) 换成加号就是多项式乘法。

Or

我们要求 \(C_i=\sum\limits_{j|k=i} A_j\times B_k\)。这个柿子长得和多项式乘法差不多,所以试图仿照 FFT 的思路对序列变换进行构造。

令 \(\text{fwt}_i(A)=\sum\limits_{j|i=i} A_j\)。考虑这样构造有什么性质:

\[\begin{aligned}
\text{fwt}_i(A)\text{fwt}_i(B)
&=(\sum_{j|i=i} A_j)(\sum_{k|i=i} B_k)\\
&=\sum_{j|i=i}\sum_{k|i=k} A_j B_k\\
&=\sum_{(j|k)|i=i} A_j B_k
\end{aligned}
\]

继续推 \(\text{fwt}(C)\):

\[\begin{aligned}
\text{fwt}_i(C)&=\sum_{j|i=i} C_j\\
&=\sum_{j|i=i}\sum_{x|y=j} A_x B_y\\
&=\sum_{(x|y)|i=i} A_x B_y\\

\end{aligned}
\]

那么有:

\[\text{fwt}_i(C)=\text{fwt}_i(A)\text{fwt}_i(B)
\]

这很好,只需要知道怎么转化 \(A\) 和 \(\text{fwt}(A)\) 就解决问题了。

\(A\to \text{fwt}(A)\)

肯定不能按定义式求,这样搞是 \(n^2\) 的。

考虑根据下标二进制下最高位的值是 \(0/1\) 对 \(A\) 序列分治。我们定义最高位为 \(0\) 的序列叫 \(A_0\),另一半叫 \(A_1\)。

那么,\(\text{fwt}(A)\) 的前半部分,\(j\) 和 \(k\) 的最高位也都只能取 \(0\),即 \(\text{fwt}(A_0)\);后半部分,\(j\) 和 \(k\) 的最高位没有限制,所以即 \(\text{fwt}(A_0)+\text{fwt}(A_1)\)。

令 \(\text{merge}(a,b)\) 表示把 \(a\) 和 \(b\) 序列前后拼接在一起,则有:

\[\text{fwt}(A)=\text{merge}(\text{fwt}(A_0),\text{fwt}(A_0)+\text{fwt}(A_1))\]

递归即可。当然更常见的写法是直接循环迭代。

\(\text{fwt}(A)\to A\)

把变换反过来,思路是差不多的:

我们这次把 \(\text{fwt}(A)\) 序列按二进制最高位分为 \(\text{fwt}(A_0)\) 和 \(\text{fwt}(A_1)\)。

同理地,\(A\) 的前半部分只由 \(\text{fwt}(A_0)\) 得到,而后半部分的 \(\text{fwt}(A_1)\) 里面两种都有,去掉 \(0\) 开头的就是 \(\text{fwt}(A_1)-\text{fwt}(A_0)\)。

那么有反演式(这里似乎也有的博客叫 \(\text{ufwt}\)):

\[\text{Ifwt}(A)=\text{merge}(\text{Ifwt}(A_0),\text{Ifwt}(A_1)-\text{Ifwt}(A_0))
\]

代码实现时可以合并这两个过程。

void Or(int *a,int tp)
{
for(int len=1;len<(1<<n);len<<=1)
for(int i=0;i<(1<<n);i+=(len<<1))
for(int j=0;j<len;j++) add(a[i+j+len],a[i+j]*tp);
}

And

和 Or 卷积本质相同,因此这里直接给出结论,读者可以自行证明。

\[\text{fwt}(A)=\text{merge}(\text{fwt}(A_0)+\text{fwt}(A_1),\text{fwt}(A_0))
\]
\[\text{Ifwt}(A)=\text{merge}(\text{Ifwt}(A_1)-\text{Ifwt}(A_0),\text{Ifwt}(A_0))
\]
void And(int *a,int tp)
{
for(int len=1;len<(1<<n);len<<=1)
for(int i=0;i<(1<<n);i+=(len<<1))
for(int j=0;j<len;j++) add(a[i+j],a[i+j+len]*tp);
}

FMT

插播一条 FMT!

不少博客说做 And 和 Or 卷积的 FWT 就是 FMT,这句话的正确性有待考据。

实际上,FMT 和 FWT 在前面构造的部分都是一样的,区别在于转化 \(A\to \text{fwt}(A)\) 的方式不同。已经知道,FWT 是基于分治加速运算,而 FMT 则是基于 DP 对这一步进行的加速。代码难度上也没有太大差别,具体是怎么加速的这里不讲了 qaq。

Xor

Xor 卷积就只能用 FWT 做了,而且也是最难理解的部分。

设 \(d(x)\) 表示 \(x\) 在二进制下 \(1\) 的个数。对 FWT 函数做如下定义:

\[\text{fwt}_i(A)=\sum_j (-1)^{d(i\&j)} A_j
\]

换句话讲,改变 \(d\) 的定义,令 \(d(x)\) 表示 \(1\) 的个数的奇偶性,偶为 \(0\),奇为 \(1\)。那么这个式子可以写成:

\[\text{fwt}_i(A)=\sum_{d(i\&j)=0} A_j-\sum_{d(i\&j)=1} A_j
\]

所以根据 \((i\text{ and }k)\text{ xor }(j\text{ and }k)=(i\text{ xor }j)\text{ and }k\),有:

\[\begin{aligned}
\text{fwt}_i(A)\text{fwt}_i(B)&=(\sum_{d(i\&j)=0} A_j-\sum_{d(i\&j)=1} A_j)(\sum_{d(i\&k)=0} B_k-\sum_{d(i\&k)=1} B_k)\\
&=\sum_{d(i\&j)\text{ xor }d(i\&k)=0}A_j B_k-\sum_{d(i\&j)\text{ xor }d(i\&k)=0}A_j B_k\\
&=\sum_{d(i\&(j\text{ xor }k))=0}A_j B_k-\sum_{d(i\&(j\text{ xor }k))=1}A_j B_k\\
&=\text{fwt}_i(C)
\end{aligned}
\]

同样基于分治思想构造 \(A\to \text{fwt}(A)\) 的转移。这个东西实在过于抽象,感性理解,理解不了就记下来(

\[\text{fwt}(A)=\text{merge}(\text{fwt}(A_0)+\text{fwt}(A_1),\text{fwt}(A_0)-\text{fwt}(A_1))
\]
\[\text{Ifwt}(A)=\text{merge}\left(\frac{\text{Ifwt}(A_0)+\text{Ifwt}(A_1)}{2},\frac{\text{Ifwt}(A_0)-\text{Ifwt}(A_1)}{2}\right)
\]

代码长得有点像 FFT。

void Xor(int *a,int tp)
{
for(int len=1;len<(1<<n);len<<=1)
for(int i=0;i<(1<<n);i+=(len<<1))
for(int j=0;j<len;j++)
{
int x=a[i+j],y=a[i+j+len];
a[i+j]=(x+y)*tp%mod,a[i+j+len]=(x-y+mod)%mod*tp%mod;
}
}

一些神秘优化

Fan facts:len 可以倒过来枚举,不影响答案。

据说会变快,但试了下好像并没有什么效果,不知道是不是我的问题。


于是这篇昨天晚上就说要写的博客咕到了现在。我是摆怪猫猫!

FWT & FMT(位运算卷积)学习笔记的更多相关文章

  1. C# 关于位运算的学习笔记

    一.理解什么是位运算 程序中的所有内容在计算机内存中都是以二进制的形式储存的(即:0或1),简单来说位运算就是直接对在内存中的二进制数的每位进行运算操作. 二.学习前先了解一下有哪些运算,运算符都怎么 ...

  2. 位运算卷积-FWT

    问题 给出两个幂级数 \(f,g\) ,求 \[ h=\sum _i\sum _jx^{i\oplus j}f_ig_j \] 其中 \(\oplus\) 是可拆分的位运算. 算法 由于位运算具有独立 ...

  3. Dirichlet 卷积学习笔记

    Dirichlet 卷积学习笔记 数论函数:数论函数亦称算术函数,一类重要的函数,指定义在正整数集上的实值或复值函数,更一般地,也可把数论函数看做是某一整数集上定义的函数. 然而百科在说什么鬼知道呢, ...

  4. @总结 - 2@ 位运算卷积/子集卷积 —— FWT/FMT

    目录 @0 - 参考资料@ @1 - 异或卷积概念及性质@ @2 - 快速沃尔什正变换(异或)@ @3 - 快速沃尔什逆变换(异或)@ @4 - 与卷积.或卷积@ @5 - 参考代码实现@ @6 - ...

  5. c#位运算小例子笔记

    关于位运算,网上有挺多好的博客介绍过,我就不多解释了 这里只记录一个小例子,是在理解位运算时候写的,帮助自己加深一下印象,做个笔记mark一下 具体场景 摇骰子游戏 1每个骰子有6个点,1-3为小,4 ...

  6. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  7. 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记

    一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...

  8. FWT快速沃尔什变换学习笔记

    FWT快速沃尔什变换学习笔记 1.FWT用来干啥啊 回忆一下多项式的卷积\(C_k=\sum_{i+j=k}A_i*B_j\) 我们可以用\(FFT\)来做. 甚至在一些特殊情况下,我们\(C_k=\ ...

  9. [学习笔记] $FWT$

    \(FWT\)--快速沃尔什变化学习笔记 知识点 \(FWT\)就是求两个多项式的位运算卷积.类比\(FFT\),\(FFT\)大多数求的卷积形式为\(c_n=\sum\limits_{i+j=n}a ...

  10. 快速沃尔什变换 (FWT)学习笔记

    证明均来自xht37 的洛谷博客 作用 在 \(OI\) 中,\(FWT\) 是用于解决对下标进行位运算卷积问题的方法. \(c_{i}=\sum_{i=j \oplus k} a_{j} b_{k} ...

随机推荐

  1. 大数据面试题集锦-Hadoop面试题(四)-YARN

    你准备好面试了吗?这里有一些面试中可能会问到的问题以及相对应的答案.如果你需要更多的面试经验和面试题,关注一下"张飞的猪大数据分享"吧,公众号会不定时的分享相关的知识和资料. 目录 ...

  2. 电脑安装JDk

    JDK软件下载链接:https://pan.baidu.com/s/1OG6wD-Fvgxu6FwuOUMDmQQ提取码:yu0l Eclipse软件下载链接:https://pan.baidu.co ...

  3. 统一观测丨使用 Prometheus 监控 Cassandra 数据库最佳实践

    作者:元格 本篇内容主要包括四部分:Cassandra 概览介绍.常见关键指标解读.常见告警规则解读.如何通过 Prometheus 建立相应监控体系. Cassandra 简介 Cassandra ...

  4. AttributeError:module‘win32com.gen_py has no attribute ‘CLSIDToClassMap‘

    解决方案如下: 1. 运行如下代码,找到文件所在位置 from win32com.client.gencache import EnsureDispatch import sys xl = Ensur ...

  5. CGLIB动态代理对象GC问题排查

    一.问题是怎么发现的 最近有个新系统开发完成后要上线,由于系统调用量很大,所以先对核心接口进行了一次压力测试,由于核心接口中基本上只有纯内存运算,所以预估核心接口的压测QPS能够达到上千. 压测容器配 ...

  6. 优化nginx参数(基本通用参数)

    全局域配置参数 worker_processes auto; worker_cpu_affinity auto; worker_rlimit_nofile 65530; 前两个参数用于开启nginx多 ...

  7. lea指令调用

    lea指令(Load Effective Address)在x86汇编语言中的作用是将一个有效地址(即一个内存地址或寄存器地址的偏移量)加载到目标寄存器中,而不是加载一个实际的内存值. lea指令的使 ...

  8. WPF 入门笔记 - 08 - 动画

    感谢大家对上篇博文的支持 回到正题,今天和大家分享下学习动画过程中的内容.动画对我来讲还是蛮新鲜的,大家知道在接触WPF之前我只用过Winform,而Winform中并没有动画的概念,当想要实现某些& ...

  9. 修改内置框架css 样式

    <style scoped> 1 <style scoped> 2 .info /deep/ .video{ // info 外层便签 /deep/ 可以理解为连接桥 .vid ...

  10. 如何获取和分析Java堆信息

    引言 在Java应用程序的开发和维护过程中,了解和分析Java堆信息是一项重要的任务.本文将介绍如何获取Java堆信息的不同方法,并提供一些分析堆信息的实用技巧. 获取Java堆信息的方法 Java虚 ...