「算法」FWT(快速沃尔什变换)
目录
- 用途
- 一些符号
- 不同位运算下的形式
- 与运算
- 代码
- 或运算
- 代码
- 异或运算
- 代码
- 同或运算
- 例题
- P4717
用途
FWTFWTFWT 用来在 O(nlog2n)O(n\log_2 n)O(nlog2n) 的时间内解决形如 Ck=∑i⊕j=kAiBjC_k=\sum_{i\oplus j=k}A_iB_jCk=∑i⊕j=kAiBj 的位运算卷积问题。
其中 ⊕\oplus⊕ 表示 and,or,xor,nxor\operatorname{and},\operatorname{or},\operatorname{xor},\operatorname{nxor}and,or,xor,nxor 等位运算中的一种。
思路和 FFTFFTFFT 类似,都是先把多项式 A,BA,BA,B 写成类似 点值表示法 的形式,分别记为 FWT(A)FWT(A)FWT(A) 和 FWT(B)FWT(B)FWT(B) ,使得 FWT(C)i=FWT(A)i⋅FWT(B)iFWT(C)_i=FWT(A)_i\cdot FWT(B)_iFWT(C)i=FWT(A)i⋅FWT(B)i 。
现在按位运算进行分类讨论。
一些符号
这里作一些规定。
- 每一个序列的长度都是二的整数幂的;
- 下文中默认位运算的优先级在 +++ 之前,即会先计算位运算,再计算加法;
- (A,B)(A,B)(A,B) 表示把 A,BA,BA,B 按 AAA 在前, BBB 在后的顺序拼接起来组成一个长度为 ∣A∣+∣B∣|A|+|B|∣A∣+∣B∣ 的序列;
- A+BA+BA+B 表示把两个长度相等的序列 A,BA,BA,B 的每一个对应下标上的数相加,即 (A+B)i=Ai+Bi(A+B)_i=A_i +B_i(A+B)i=Ai+Bi ,形成的序列长度为 ∣A∣|A|∣A∣ ;
- A−BA-BA−B 表示把两个长度相等的序列 A,BA,BA,B 的每一个对应下标上的数相减,即 (A−B)i=Ai−Bi(A-B)_i=A_i -B_i(A−B)i=Ai−Bi ,形成的序列长度为 ∣A∣|A|∣A∣ 。
不同位运算下的形式
与运算
先给出一个结论:FWT(A)i=∑iandj=iAjFWT(A)_i=\sum_{i\operatorname{and} j=i} A_jFWT(A)i=iandj=i∑Aj
证明:
∀i∈[0,∣A∣)∪Z,都有FWT(A)i×FWT(B)i=FWT(C)i⟸(∑jandi=iAj)(∑kandi=iBk)=∑jandkandi=iAjBk⟸{jandi=i,kandi=i等价于jandkandi=i\begin{aligned} &\forall i\in[0,|A|)\cup Z,\text{都有}\\ &\qquad\qquad FWT(A)_i\times FWT(B)_i=FWT(C)_i\\ &\impliedby\left(\sum_{j\operatorname{and} i=i}A_j\right)\left(\sum_{k\operatorname{and} i=i}B_k \right) =\sum_{j\operatorname{and} k\operatorname{and} i=i}A_jB_k\\ &\impliedby\begin{cases} j\operatorname{and} i&=i,\\ k\operatorname{and} i&=i \end{cases} \quad\text{等价于}\quad j\operatorname{and} k\operatorname{and} i=i \end{aligned}∀i∈[0,∣A∣)∪Z,都有FWT(A)i×FWT(B)i=FWT(C)i⟸⎝⎛jandi=i∑Aj⎠⎞(kandi=i∑Bk)=jandkandi=i∑AjBk⟸{jandikandi=i,=i等价于jandkandi=i
得证。
根据这个结论直接做是 O(n2)O\left(n^2\right)O(n2) 的。
考虑像 FFTFFTFFT 那样,把序列 AAA 二分为序列 A0,A1A_0,A_1A0,A1 ,再根据 FWT(A0),FWT(A1)FWT(A_0),FWT(A_1)FWT(A0),FWT(A1) 算出 FWT(A)FWT(A)FWT(A) ,这样就可以做到 O(nlog2n)O(n\log_2 n)O(nlog2n) 了。
发现如果把序列 AAA 的下标用 二进制 表示, A0A_0A0 是由下标中第 1+log2∣A∣1+\log_2 |A|1+log2∣A∣ 位(也就是当前的最高位)为 000 的元素组成的, A1A_1A1 中的元素的最高位则为 111 。
因为 and\operatorname{and}and 运算有这样的性质:除了 1and1=11\operatorname{and} 1=11and1=1 ,其它 0and1,0and0,1and00\operatorname{and} 1,0\operatorname{and} 0,1\operatorname{and} 00and1,0and0,1and0 的值都为 000 。即 A1A_1A1 会贡献到 A0A_0A0 ,因此可以得到:
FWT(A)=(FWT(A0)+FWT(A1),FWT(A1))FWT(A)=(FWT(A_0)+FWT(A_1),FWT(A_1))FWT(A)=(FWT(A0)+FWT(A1),FWT(A1))
那反演就容易了:
UFWT(A)=(UFWT(A0)−UFWT(A1),UFWT(A1))UFWT(A)=(UFWT(A_0)-UFWT(A_1),UFWT(A_1))UFWT(A)=(UFWT(A0)−UFWT(A1),UFWT(A1))
代码
inline void fwt_and(int f[],int opt)
{for(int i=2,j=1;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<j+t;++k)f[k]+=f[k+j]*opt;
}
或运算
同样的,先给出结论:FWT(A)i=∑iorj=iAjFWT(A)_i=\sum_{i\operatorname{or} j=i} A_jFWT(A)i=iorj=i∑Aj
证明是类似的,就不写了。结论也是类似的:
FWT(A)=(FWT(A0),FWT(A0)+FWT(A1))UFWT(A)=(UFWT(A0),UFWT(A1)−UFWT(A0))\begin{aligned} FWT(A)&=(FWT(A_0),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=(UFWT(A_0),UFWT(A_1)-UFWT(A_0)) \end{aligned}FWT(A)UFWT(A)=(FWT(A0),FWT(A0)+FWT(A1))=(UFWT(A0),UFWT(A1)−UFWT(A0))
代码
inline void fwt_or(int f[],int opt)
{for(int i=2,j=1;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<j+t;++k)f[k+j]+=f[k]*opt;
}
异或运算
注意这个结论长得不一样( popcount(x)\operatorname{popcount}(x)popcount(x) 表示把 xxx 写成二进制形式有多少位上的数为 111 ):
FWT(A)i=∑2∣popcount(iandj)Aj−∑2∤popcount(iandj)AjFWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{and} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{and} j)} A_jFWT(A)i=2∣popcount(iandj)∑Aj−2∤popcount(iandj)∑Aj
注意不是直接仿照 与运算 、 或运算 的形式写成
FWT(A)i=∑ixorj=iAjFWT(A)_i=\sum_{i\operatorname{xor} j=i} A_jFWT(A)i=ixorj=i∑Aj
为了方便起见,定义运算符 ⊗\otimes⊗ : x⊗y=popcount(xandy)mod2x\otimes y=\operatorname{popcount}(x\operatorname{and} y)\mod{2}x⊗y=popcount(xandy)mod2 。
那么这个运算符是满足以下性质的:
(x⊗y)xor(x⊗z)=x⊗(yxorz)(x\otimes y)\operatorname{xor}(x\otimes z)=x\otimes (y\operatorname{xor} z)(x⊗y)xor(x⊗z)=x⊗(yxorz)
证明:
令 a=popcount(xandyandz),b=popcount(xxory)−a,c=popcount(yxorz)−aa=\operatorname{popcount}(x\operatorname{and}y\operatorname{and}z),b=\operatorname{popcount}(x\operatorname{xor}y)-a,c=\operatorname{popcount}(y\operatorname{xor}z)-aa=popcount(xandyandz),b=popcount(xxory)−a,c=popcount(yxorz)−a 。
那么
popcount(xand(yxorz))=b+c=popcount(xxory)+popcount(xxorz)−2a∴x⊗(yxorz)=(x⊗y+x⊗z)mod2=(x⊗y)xor(x⊗z)\begin{aligned} &\quad\;\operatorname{popcount}(x\operatorname{and}(y\operatorname{xor}z))\\&=b+c\\ &=\operatorname{popcount}(x\operatorname{xor}y)+\operatorname{popcount}(x\operatorname{xor}z)-2a\\ &\therefore x\otimes(y\operatorname{xor}z)\\ &=(x\otimes y+x\otimes z)\mod{2}\\ &=(x\otimes y)\operatorname{xor}(x\otimes z) \end{aligned}popcount(xand(yxorz))=b+c=popcount(xxory)+popcount(xxorz)−2a∴x⊗(yxorz)=(x⊗y+x⊗z)mod2=(x⊗y)xor(x⊗z)
得证。
然后现在来证明结论:
证明:
FWT(A)i⋅FWT(B)i=(∑j⊗i=0Aj−∑j⊗i=1Aj)(∑k⊗i=0Bk−∑k⊗i=1Bk)=(∑j⊗i=0Aj)(∑k⊗i=0Bk)−(∑j⊗i=0Aj)(∑k⊗i=1Bk)−(∑j⊗i=1Aj)(∑k⊗i=0Bk)+(∑j⊗i=1Aj)(∑k⊗i=1Bk)=∑i⊗(jxork)=0AjBk−∑i⊗(jxork)=1AjBk=FWT(C)i\begin{aligned} &FWT(A)_i\cdot FWT(B)_i\\ =&\left(\sum_{j\otimes i=0}A_j-\sum_{j\otimes i=1}A_j \right)\left(\sum_{k\otimes i=0}B_k-\sum_{k\otimes i=1}B_k \right)\\ =&\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)-\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)-\\ &\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)+\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)\\ =&\sum_{i\otimes(j\operatorname{xor}k)=0}A_jB_k-\sum_{i\otimes(j\operatorname{xor}k)=1}A_jB_k\\ =&FWT(C)_i \end{aligned}====FWT(A)i⋅FWT(B)i(j⊗i=0∑Aj−j⊗i=1∑Aj)(k⊗i=0∑Bk−k⊗i=1∑Bk)(j⊗i=0∑Aj)(k⊗i=0∑Bk)−(j⊗i=0∑Aj)(k⊗i=1∑Bk)−(j⊗i=1∑Aj)(k⊗i=0∑Bk)+(j⊗i=1∑Aj)(k⊗i=1∑Bk)i⊗(jxork)=0∑AjBk−i⊗(jxork)=1∑AjBkFWT(C)i
得证。
因此可以得出:
FWT(A)=(FWT(A0)+FWT(A1),FWT(A0)−FWT(A1))UFWT(A)=(UFWT(A1)+UFWT(A0)2,UFWT(A1)−UFWT(A0)2)\begin{aligned} FWT(A)&=(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)+UFWT(A_0)}{2},\cfrac{UFWT(A_1)-UFWT(A_0)}{2}\right) \end{aligned}FWT(A)UFWT(A)=(FWT(A0)+FWT(A1),FWT(A0)−FWT(A1))=(2UFWT(A1)+UFWT(A0),2UFWT(A1)−UFWT(A0))
代码
这份代码里面的 FWTFWTFWT 是在取模意义下的,不用取模的话修改一下就好了。
inline int plus(int x,int y)
{x+=y;if(x>=P) x-=P;else if(x<0) x+=P;return x;
}
inline void fwt_xor(int f[],int opt)
{for(int i=2,j=1,x,y;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<t+j;++k){x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]);opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ;}
}
同或运算
同或运算下的 FWTFWTFWT 与异或的类似。
先给出结论: FWT(A)i=∑2∣popcount(iorj)Aj−∑2∤popcount(iorj)AjFWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{or} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{or} j)} A_jFWT(A)i=2∣popcount(iorj)∑Aj−2∤popcount(iorj)∑Aj
可以得到
FWT(A)=(FWT(A0)−FWT(A1),FWT(A0)+FWT(A1))UFWT(A)=(UFWT(A1)−UFWT(A0)2,UFWT(A1)+UFWT(A0)2)\begin{aligned} FWT(A)&=(FWT(A_0)-FWT(A_1),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)-UFWT(A_0)}{2},\cfrac{UFWT(A_1)+UFWT(A_0)}{2}\right) \end{aligned}FWT(A)UFWT(A)=(FWT(A0)−FWT(A1),FWT(A0)+FWT(A1))=(2UFWT(A1)−UFWT(A0),2UFWT(A1)+UFWT(A0))
在异或运算的代码上改一下可以了。
例题
P4717
P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)
#include<cstdio>
#include<cstring>
using namespace std;
#define fo(i,l,r) for(i=l;i<r;++i)
#define N 262145
const int P=998244353;
const long long inv2=499122177;
int m,a[N],b[N],c[N],A[N],B[N];
inline void add(int &x,int y)
{x+=y;if(x>=P) x-=P;else if(x<0) x+=P;
}
inline int plus(int x,int y)
{x+=y;if(x>=P) x-=P;else if(x<0) x+=P;return x;
}
inline void fwt_or(int f[],int opt)
{for(int i=2,j=1;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<j+t;++k)add(f[k+j],f[k]*opt);
}
inline void fwt_and(int f[],int opt)
{for(int i=2,j=1;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<j+t;++k)add(f[k],f[k+j]*opt);
}
inline void fwt_xor(int f[],int opt)
{for(int i=2,j=1,x,y;i<=m;j=i,i<<=1)for(int t=0;t<m;t+=i)for(int k=t;k<t+j;++k){x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]);opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ;}
}
int main()
{freopen("fwt.in","r",stdin);freopen("fwt.out","w",stdout);int n,i;scanf("%d",&n),m=1<<n;fo(i,0,m) scanf("%d",a+i);fo(i,0,m) scanf("%d",b+i);fo(i,0,m) A[i]=a[i],B[i]=b[i];fwt_or(a,1),fwt_or(b,1);fo(i,0,m) c[i]=1LL*a[i]*b[i]%P;fwt_or(c,-1);fo(i,0,m) printf("%d ",c[i]);puts("");memcpy(a,A,sizeof a),memcpy(b,B,sizeof b);fwt_and(a,1),fwt_and(b,1);fo(i,0,m) c[i]=1LL*a[i]*b[i]%P;fwt_and(c,-1);fo(i,0,m) printf("%d ",c[i]);puts("");memcpy(a,A,sizeof a),memcpy(b,B,sizeof b);fwt_xor(a,1),fwt_xor(b,1);fo(i,0,m) c[i]=1LL*a[i]*b[i]%P;fwt_xor(c,-1);fo(i,0,m) printf("%d ",c[i]);puts("");return 0;
}
「算法」FWT(快速沃尔什变换)相关推荐
- [多项式算法](Part 4)FWT 快速沃尔什变换 学习笔记
其他多项式算法传送门: [多项式算法](Part 1)FFT 快速傅里叶变换 学习笔记 [多项式算法](Part 2)NTT 快速数论变换 学习笔记 [多项式算法](Part 3)MTT 任意模数FF ...
- FWT(快速沃尔什变换)零基础详解qaq(ACM/OI)
1.前言(废话) 记得一年半之前做SRM518 Nim的时候还不知道FWT,当时自己用分治完美的水过去了.然后昨天的牛客有一道题,是说nim博弈中有n堆石子,请问最多取出多少堆石子可以让先手必败.当时 ...
- To_Heart—总结——FWT(快速沃尔什变换)
目录 闲话 拿来求什么 或 与 异或 闲话 这个比FFT简单了很多呢,,大概是我可以学懂的水平! 好像是叫 快速沃尔什变换 ? 拿来求什么 以 FFT 来类比.我们 FFT 可以在 O ( n l o ...
- 「技巧」如何快速安装 Sketch 插件
Sketch拥有强大丰富的插件,但是这些插件天各一方,四处查找下载地址非常麻烦.这里提供一个技巧,通过一个入口可以安装各种插件,基本涵盖了市面上所有靠谱的插件. 准备 Sketch54 Runner ...
- [FWT] 时隔一年再回首FWT(快速沃尔什变换),我终于不再是个门外汉
时隔一年再回首FWT,我似乎有了新理解?? 添加了原理的推导,以前就只有模板- 文章目录 引入 or(或)卷积 原理 FWT_or正变换 FWT_or逆变换 模板 and(与)卷积 原理 FWT_an ...
- 最长路径算法 c语言_「算法」求二叉树的最长同值路径
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值. 这条路径可以经过也可以不经过根节点. 注意:两个节点之间的路径长度由它们之间的边数表示. 示例 1: 输入: 输出: 2 示例 2: ...
- 递归算法1加到100_「算法」北京大学算法基础—递归(1)
(干货菌:枚举和递归是所有算法的根基,所有算法也都是这两种思维的继续拓展.) 递归:某个函数直接或间接的调用自身. f(x)=g(f(x-1)),已知f(0)的值和g(),就可以求出. 枚举是把一个问 ...
- 「算法」 关于随机化排序算法
目录 前言 算法介绍 思路 缺陷及优化 优化1(效果不明显) 优化2(失败) 优化3(成功) 时间复杂度 代码 前言 GDKOI2021 的某次直播期间,看到讨论区里有人提到了 随机排序 算法( Mo ...
- 2台电脑一根网线传文件_「教程」如何快速的在两台电脑间传输大文件?
两台电脑如何传输文件 我们常见的就是使用U盘或者网盘进行传输 但是速度非常的慢 我们需要一种文件可以直接复制到另一台设备的方法 首先准备一根网线 然后将两台电脑连接到同一局域网内 在右下角的小电脑打开 ...
- 「算法」拓扑排序(货真价实,童叟无欺)
文章目录 什么是拓扑排序 模拟 代码实现 BFS DFS 什么是拓扑排序 对一个有向无环图(DirectedAcyclicGraph简称DAG)(Directed Acyclic Graph简称DA ...
最新文章
- php mysql预处理_php mysqli扩展之预处理
- 我来做百科(第九天)
- GoEasy导入依赖的时候报错,包用不了,maven导包
- 产品经理面试中如何攻克有关用户体验的问题?
- php对称字符串,PHP实现简单的对称加密和解密方法 - str_split
- android 代码设置dialog 全屏,Android里把Dialog设置为全屏的方法
- 植物病害分类的深度可解释体系结构(github源码)
- C语言:编写一个程序,打印乘法“九九表”
- Layui 左部菜单栏无限级分类
- 查看linux的机器内存大小,linux 查看机器内存方法 (free命令)
- 伯克利神课 CS61B 游戏项目 Project3 建立我的世界(地图生成)
- 腾讯云实验室:搭建 LNMP 环境
- 猎聘网推出移动互联求职新方式
- react-ant 实现二级路由和三级路由
- 全国院线总票房破50亿!影院复工后,哪些电影最受欢迎?可视化案例
- aardio工程实例——MIDI音乐盒(源码)
- c语言表示注释,c语言中注释的位置
- 笔记本计算机涂硅脂,硅脂,小编教你怎么在电脑CPU上涂散热硅脂
- 不服不行!在这份MySQL文档面前,别的数据库学习资料都是拉基
- wps里的html怎么保存,用WPS 2012保存网页内容的实用方法
热门文章
- 关于网站项目计划书的写法
- 如果你现在没有目标,或许很迷茫
- (附源码)Springboot酒店会员点餐系统 毕业设计 072005
- java7723魂斗罗2_魂斗罗3代-完全版
- html5 显示k线图,canvas绘图,html5 k线图,股票行情图
- 云服务器修复漏洞用重启吗,漏洞修复后要重启吗
- 【115@365】三国杀开源系列之六-入口文件解读
- #linux# gcc编译优化-O0 -O1 -O2 -O3 -OS说明
- SLAM综述阅读笔记一:Past, Present, and Future of Simultaneous Localization And Mapping(2016)
- 网站被黑被劫持跳转到其他网站该如何解决