目录

  • 用途
  • 一些符号
  • 不同位运算下的形式
    • 与运算
      • 代码
    • 或运算
      • 代码
    • 异或运算
      • 代码
    • 同或运算
  • 例题
    • P4717

用途

FWTFWTFWT 用来在 O(nlog⁡2n)O(n\log_2 n)O(nlog2​n) 的时间内解决形如 Ck=∑i⊕j=kAiBjC_k=\sum_{i\oplus j=k}A_iB_jCk​=∑i⊕j=k​Ai​Bj​ 的位运算卷积问题。

其中 ⊕\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​ 。

现在按位运算进行分类讨论。

一些符号

这里作一些规定。

  1. 每一个序列的长度都是二的整数幂的;
  2. 下文中默认位运算的优先级在 +++ 之前,即会先计算位运算,再计算加法;
  3. (A,B)(A,B)(A,B) 表示把 A,BA,BA,B 按 AAA 在前, BBB 在后的顺序拼接起来组成一个长度为 ∣A∣+∣B∣|A|+|B|∣A∣+∣B∣ 的序列;
  4. 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∣ ;
  5. 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=∑iand⁡j=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⟸(∑jand⁡i=iAj)(∑kand⁡i=iBk)=∑jand⁡kand⁡i=iAjBk⟸{jand⁡i=i,kand⁡i=i等价于jand⁡kand⁡i=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∑​Aj​Bk​⟸{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(nlog⁡2n)O(n\log_2 n)O(nlog2​n) 了。

发现如果把序列 AAA 的下标用 二进制 表示, A0A_0A0​ 是由下标中第 1+log⁡2∣A∣1+\log_2 |A|1+log2​∣A∣ 位(也就是当前的最高位)为 000 的元素组成的, A1A_1A1​ 中的元素的最高位则为 111 。

因为 and⁡\operatorname{and}and 运算有这样的性质:除了 1and⁡1=11\operatorname{and} 1=11and1=1 ,其它 0and⁡1,0and⁡0,1and⁡00\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=∑ior⁡j=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⁡(iand⁡j)Aj−∑2∤popcount⁡(iand⁡j)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=∑ixor⁡j=iAjFWT(A)_i=\sum_{i\operatorname{xor} j=i} A_jFWT(A)i​=ixorj=i∑​Aj​

为了方便起见,定义运算符 ⊗\otimes⊗ : x⊗y=popcount⁡(xand⁡y)mod2x\otimes y=\operatorname{popcount}(x\operatorname{and} y)\mod{2}x⊗y=popcount(xandy)mod2 。

那么这个运算符是满足以下性质的:

(x⊗y)xor⁡(x⊗z)=x⊗(yxor⁡z)(x\otimes y)\operatorname{xor}(x\otimes z)=x\otimes (y\operatorname{xor} z)(x⊗y)xor(x⊗z)=x⊗(yxorz)

证明:

令 a=popcount⁡(xand⁡yand⁡z),b=popcount⁡(xxor⁡y)−a,c=popcount⁡(yxor⁡z)−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⁡(yxor⁡z))=b+c=popcount⁡(xxor⁡y)+popcount⁡(xxor⁡z)−2a∴x⊗(yxor⁡z)=(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⊗(jxor⁡k)=0AjBk−∑i⊗(jxor⁡k)=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∑​Aj​Bk​−i⊗(jxork)=1∑​Aj​Bk​FWT(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⁡(ior⁡j)Aj−∑2∤popcount⁡(ior⁡j)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(快速沃尔什变换)相关推荐

  1. [多项式算法](Part 4)FWT 快速沃尔什变换 学习笔记

    其他多项式算法传送门: [多项式算法](Part 1)FFT 快速傅里叶变换 学习笔记 [多项式算法](Part 2)NTT 快速数论变换 学习笔记 [多项式算法](Part 3)MTT 任意模数FF ...

  2. FWT(快速沃尔什变换)零基础详解qaq(ACM/OI)

    1.前言(废话) 记得一年半之前做SRM518 Nim的时候还不知道FWT,当时自己用分治完美的水过去了.然后昨天的牛客有一道题,是说nim博弈中有n堆石子,请问最多取出多少堆石子可以让先手必败.当时 ...

  3. To_Heart—总结——FWT(快速沃尔什变换)

    目录 闲话 拿来求什么 或 与 异或 闲话 这个比FFT简单了很多呢,,大概是我可以学懂的水平! 好像是叫 快速沃尔什变换 ? 拿来求什么 以 FFT 来类比.我们 FFT 可以在 O ( n l o ...

  4. 「技巧」如何快速安装 Sketch 插件

    Sketch拥有强大丰富的插件,但是这些插件天各一方,四处查找下载地址非常麻烦.这里提供一个技巧,通过一个入口可以安装各种插件,基本涵盖了市面上所有靠谱的插件. 准备 Sketch54 Runner ...

  5. [FWT] 时隔一年再回首FWT(快速沃尔什变换),我终于不再是个门外汉

    时隔一年再回首FWT,我似乎有了新理解?? 添加了原理的推导,以前就只有模板- 文章目录 引入 or(或)卷积 原理 FWT_or正变换 FWT_or逆变换 模板 and(与)卷积 原理 FWT_an ...

  6. 最长路径算法 c语言_「算法」求二叉树的最长同值路径

    给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值. 这条路径可以经过也可以不经过根节点. 注意:两个节点之间的路径长度由它们之间的边数表示. 示例 1: 输入: 输出: 2 示例 2: ...

  7. 递归算法1加到100_「算法」北京大学算法基础—递归(1)

    (干货菌:枚举和递归是所有算法的根基,所有算法也都是这两种思维的继续拓展.) 递归:某个函数直接或间接的调用自身. f(x)=g(f(x-1)),已知f(0)的值和g(),就可以求出. 枚举是把一个问 ...

  8. 「算法」 关于随机化排序算法

    目录 前言 算法介绍 思路 缺陷及优化 优化1(效果不明显) 优化2(失败) 优化3(成功) 时间复杂度 代码 前言 GDKOI2021 的某次直播期间,看到讨论区里有人提到了 随机排序 算法( Mo ...

  9. 2台电脑一根网线传文件_「教程」如何快速的在两台电脑间传输大文件?

    两台电脑如何传输文件 我们常见的就是使用U盘或者网盘进行传输 但是速度非常的慢 我们需要一种文件可以直接复制到另一台设备的方法 首先准备一根网线 然后将两台电脑连接到同一局域网内 在右下角的小电脑打开 ...

  10. 「算法」拓扑排序(货真价实,童叟无欺)

    文章目录 什么是拓扑排序 模拟 代码实现 BFS DFS 什么是拓扑排序  对一个有向无环图(DirectedAcyclicGraph简称DAG)(Directed Acyclic Graph简称DA ...

最新文章

  1. php mysql预处理_php mysqli扩展之预处理
  2. 我来做百科(第九天)
  3. GoEasy导入依赖的时候报错,包用不了,maven导包
  4. 产品经理面试中如何攻克有关用户体验的问题?
  5. php对称字符串,PHP实现简单的对称加密和解密方法 - str_split
  6. android 代码设置dialog 全屏,Android里把Dialog设置为全屏的方法
  7. 植物病害分类的深度可解释体系结构(github源码)
  8. C语言:编写一个程序,打印乘法“九九表”
  9. Layui 左部菜单栏无限级分类
  10. 查看linux的机器内存大小,linux 查看机器内存方法 (free命令)
  11. 伯克利神课 CS61B 游戏项目 Project3 建立我的世界(地图生成)
  12. 腾讯云实验室:搭建 LNMP 环境
  13. 猎聘网推出移动互联求职新方式
  14. react-ant 实现二级路由和三级路由
  15. 全国院线总票房破50亿!影院复工后,哪些电影最受欢迎?可视化案例
  16. aardio工程实例——MIDI音乐盒(源码)
  17. c语言表示注释,c语言中注释的位置
  18. 笔记本计算机涂硅脂,硅脂,小编教你怎么在电脑CPU上涂散热硅脂
  19. 不服不行!在这份MySQL文档面前,别的数据库学习资料都是拉基
  20. wps里的html怎么保存,用WPS 2012保存网页内容的实用方法

热门文章

  1. 关于网站项目计划书的写法
  2. 如果你现在没有目标,或许很迷茫
  3. (附源码)Springboot酒店会员点餐系统 毕业设计 072005
  4. java7723魂斗罗2_魂斗罗3代-完全版
  5. html5 显示k线图,canvas绘图,html5 k线图,股票行情图
  6. 云服务器修复漏洞用重启吗,漏洞修复后要重启吗
  7. 【115@365】三国杀开源系列之六-入口文件解读
  8. #linux# gcc编译优化-O0 -O1 -O2 -O3 -OS说明
  9. SLAM综述阅读笔记一:Past, Present, and Future of Simultaneous Localization And Mapping(2016)
  10. 网站被黑被劫持跳转到其他网站该如何解决