FWT & FMT(位运算卷积)学习笔记
它们两个的全名叫 快速沃尔什变换(FWT) 和 快速莫比乌斯变换(FMT),用来在 时间复杂度内求位运算卷积。
因为 FMT 能解决的问题是 FWT 的子集,所以这里不讲 FMT,把它拎出来是想说它们两个的区别。
参考资料:偶耶XJX-浅谈快速沃尔什变换(FWT)&快速莫比乌斯变换(FMT)、zcxxn-多项式学习笔记。
位运算卷积
位运算卷积,就是求下面这个问题:
给定长度为 的序列 ,求它们的卷积 ,其中 为 中的一种。这里如果把 换成加号就是多项式乘法。
Or
我们要求 。这个柿子长得和多项式乘法差不多,所以试图仿照 FFT 的思路对序列变换进行构造。
令 。考虑这样构造有什么性质:
继续推 :
那么有:
这很好,只需要知道怎么转化 和 就解决问题了。
肯定不能按定义式求,这样搞是 的。
考虑根据下标二进制下最高位的值是 对 序列分治。我们定义最高位为 的序列叫 ,另一半叫 。
那么, 的前半部分, 和 的最高位也都只能取 ,即 ;后半部分, 和 的最高位没有限制,所以即 。
令 表示把 和 序列前后拼接在一起,则有:
递归即可。当然更常见的写法是直接循环迭代。
把变换反过来,思路是差不多的:
我们这次把 序列按二进制最高位分为 和 。
同理地, 的前半部分只由 得到,而后半部分的 里面两种都有,去掉 开头的就是 。
那么有反演式(这里似乎也有的博客叫 ):
代码实现时可以合并这两个过程。
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 卷积本质相同,因此这里直接给出结论,读者可以自行证明。
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 在前面构造的部分都是一样的,区别在于转化 的方式不同。已经知道,FWT 是基于分治加速运算,而 FMT 则是基于 DP 对这一步进行的加速。代码难度上也没有太大差别,具体是怎么加速的这里不讲了 qaq。
Xor
Xor 卷积就只能用 FWT 做了,而且也是最难理解的部分。
设 表示 在二进制下 的个数。对 FWT 函数做如下定义:
换句话讲,改变 的定义,令 表示 的个数的奇偶性,偶为 ,奇为 。那么这个式子可以写成:
所以根据 ,有:
同样基于分治思想构造 的转移。这个东西实在过于抽象,感性理解,理解不了就记下来(
代码长得有点像 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 可以倒过来枚举,不影响答案。
据说会变快,但试了下好像并没有什么效果,不知道是不是我的问题。
于是这篇昨天晚上就说要写的博客咕到了现在。我是摆怪猫猫!
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/17687332.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律