在多项式卷积的处理中,我们实际上实现的是下面的一个式子

\[C_k=\sum_{i+j=k}A_iB_j
\]

然而事实上有些和(sang)蔼(xin)可(bing)亲(kuang)的出题人,并不会让你直接求这样的式子,比如把+换成-什么的

但是更多时候,你拿到手上的却是这样一个式子

\[C_k=\sum_{i\bigoplus j=k}A_iB_j
\]

其中\(\bigoplus\)可能是\(or,and,xor\)中的一种或多种

那么现在我们就会使用FWT(快速沃尔什变化)来优化时间复杂度

前置芝士

FFT

理解思想即可,不在赘述

正文

(p.s. 注意下文的乘号应结合具体语境理解,是表示按位相乘还是位运算卷积)

考虑我们在实现FFT的时候干了什么?

我们搞出了一个点值表示法,然后直接对点值进行求乘积,再把它转回去

那么这对位运算是否成立呢?

即我们希望有\(FWT(C)=FWT(A)\times FWT(B)\),此时可以直接按位相乘

直觉告诉我们是有的,但是是什么样的呢?

or

你要问我是怎么找的我也无从说起,十分诡异

想要了解更多的可以参考:https://www.cnblogs.com/ACMLCZH/p/8022502.html

这里直接给出结论及其正确性的证明

对于一个有\(2^n\)项的\(A_i\)

\[FWT(A)=\begin{cases}A & n=0 \\ (FWT(A_0),FWT(A_0+A_1)) & n>0 \end{cases}
\]

什么是\(A_0\)和\(A_1\)?

我们把\(A\)想象成一个\(2^n\)维的向量,那么前\(2^{n-1}\)项是\(A_0\),后\(2^{n-1}\)项是\(A_1\)

这之后的括号表示的意义同样(在之后会阐述\(FWT(A)\)的本质)

如果只是感性理解这玩意还是比较容易的:对于\(A_0\)中的每一项,它们的二进制表示最高位均是\(0\),当它们与\(A_1\)的某一项进行了\(or\)卷积的话,那么最高位就变成了1,对后面的\(2^{n-1}\)项产生了贡献

那么问题就变成了如何证明这时的\(FWT(C)=FWT(A)\times FWT(B)\)

我们考虑使用数学归纳法证明,设此时\(A,B,C\)均为\(2^n\)项

若\(n=0\),结论显然成立

若\(n>0\),假设结论在\(n=k\)时成立

那么此时就应该有(注意这里用\(*\)表示\(or\)卷积)

\[FWT(A_0*B_0)=FWT(A_0)\times FWT(B_0)\\
FWT(A_1*B_0)=FWT(A_1)\times FWT(B_0)\\
FWT(A_0*B_1)=FWT(A_0)\times FWT(B_1)\\
FWT(A_1*B_1)=FWT(A_1)\times FWT(B_1)
\]

(理解一下,在括号里面的表示位运算卷积,外面的则为按位相乘)

同时显然\(FWT(A+B)=FWT(A)+FWT(B)\)(+表示按位相加)

首先是等式左边

\[C=(A_0*B_0,A_1*B_0+A_0*B_1+A_1*B_1)\\
\]

这也很好理解,和上面\(FWT\)的公式一样理解

\[\begin{aligned}
FWT(C)=&(FWT(A_0*B_0),FWT(A_0*B_0)+FWT(A_0*B_1+A_1*B_0+A_1*B_1))\\
=&(FWT(A_0*B_0),FWT(A_0*B_0)+FWT(A_0*B_1)+FWT(A_1*B_0)+FWT(A_1*B_1))\\
=&(FWT(A_0)\times FWT(B_0),FWT(A_0)\times FWT(B_0)+FWT(A_1)\times FWT(B_1)+FWT(A_1)\times FWT(B_0)+FWT(A_1)\times FWT(B_1)\\
\end{aligned}
\]

再来看等式左边

\[\begin{aligned}
FWT(A)\times FWT(B)=&(FWT(A_0),FWT(A_0+A_1))\times(FWT(B_0),FWT(B_0+B_1))\\
=&(FWT(A_0)\times FWT(B_0),(FWT(A_0)+FWT(A_1))\times(FWT(B_0)+FWT(B_1)))\\
=&(FWT(A_0)\times FWT(B_0),FWT(A_0)\times FWT(B_0)+FWT(A_0)\times FWT(B_1)+FWT(A_1)\times FWT(B_0)+FWT(A_1)\times FWT(B_1))
\end{aligned}
\]

证毕

好像结束了?让我们来看一下\(FWT(A)\)的本质

回到最开始的式子\(c_i=\sum_{j|k=i}a_jb_k\),假设我们记\(C_i=\sum _{j\subseteq i}c_j\),则

\[\begin{aligned}
C_i=&\sum_{j|k\subseteq i}a_ib_j\\
=&\sum_{j\subseteq i,k\subseteq i}a_ib_j\\
=&A_jB_k
\end{aligned}
\]

我们发现\(A,B,C\)就起到了上文中\(FWT\)的作用,于是当我们要求某个集合的子集中的元素和时,除了\(O(3^n)\)的枚举子集以外,还有\(O(n2^n)\)的求出数组\(A\)的\(FWT(A)\)的方法,\(A_i\)的\(i\)的子集和就是\(FWT(A)_i\)

and

证明和上面是类似的,在这里直接给出结论

\[FWT(A)=\begin{cases}A & n=0 \\ (FWT(A_0+A_1),FWT(A_1)) & n>0 \end{cases}
\]

以及我们注意到,当求出\(FWT(A)\)时,我们有\(FWT(A)_i=\sum_{i\subseteq j}A_j\),它和上面的\(or\)卷积在其他除了FWT的题目中也有很好的应用

xor

依然是只给出结论,证明留给读者

\[FWT(A)=\begin{cases}A & n=0 \\ (FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1)) & n>0 \end{cases}
\]

IDFT相关

我们实现了FFT中类似于DFT的一步,那么IDFT呢?

直接按照上面的逆运算做回去就可以了,直接上结论:

对于\(or\)卷积

\[IFWT(A)=\begin{cases}A& n=0 \\ (IFWT(A_0),IFWT(A_1)-IFWT(A_0)) & n>0 \end{cases}
\]

对于\(and\)卷积

\[IFWT(A)=\begin{cases}A& n=0 \\ (IFWT(A_0)-IFWT(A_1),IFWT(A_1))& n>0\end{cases}
\]

对于\(xor\)卷积

\[IFWT(A)=\begin{cases} A& n=0\\ (\frac{IFWT(A_0)+IFWT(A_1)}{2},\frac{IFWT(A_0)-IFWT(A_1)}{2}\end{cases}
\]

code

模板题:luogu 4717

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
const ll maxd=998244353,inv2=499122177;
const double pi=acos(-1.0);
int n,N;
ll a[1001000],b[1001000],c[1001000]; int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
} void fwt_or(int lim,ll *a,int typ)
{
int mid;
for (mid=1;mid<lim;mid<<=1)
{
int len=(mid<<1),sta,j;
for (sta=0;sta<lim;sta+=len)
{
for (j=0;j<mid;j++)
{
if (typ==1) a[sta+mid+j]=(a[sta+j+mid]+a[sta+j])%maxd;
else a[sta+mid+j]=(a[sta+j+mid]+maxd-a[sta+j])%maxd;
}
}
}
} void fwt_and(int lim,ll *a,int typ)
{
int mid;
for (mid=1;mid<lim;mid<<=1)
{
int len=(mid<<1),sta,j;
for (sta=0;sta<lim;sta+=len)
{
for (j=0;j<mid;j++)
{
if (typ==1) a[sta+j]=(a[sta+j]+a[sta+mid+j])%maxd;
else a[sta+j]=(a[sta+j]+maxd-a[sta+j+mid])%maxd;
}
}
}
} void fwt_xor(int lim,ll *a,int typ)
{
int mid;
for (mid=1;mid<lim;mid<<=1)
{
int len=(mid<<1),sta,j;
for (sta=0;sta<lim;sta+=len)
{
for (j=0;j<mid;j++)
{
int x=a[sta+j],y=a[sta+j+mid];
a[sta+j]=(x+y)%maxd;
a[sta+j+mid]=(x+maxd-y)%maxd;
if (typ==-1) {a[sta+j]=(a[sta+j]*inv2)%maxd;a[sta+j+mid]=(a[sta+j+mid]*inv2)%maxd;}
}
}
}
} int main()
{
n=read();N=1<<n;int i;
for (i=0;i<N;i++) a[i]=read();
for (i=0;i<N;i++) b[i]=read();
fwt_or(N,a,1);fwt_or(N,b,1);
for (i=0;i<N;i++) c[i]=(a[i]*b[i])%maxd;
fwt_or(N,a,-1);fwt_or(N,b,-1);fwt_or(N,c,-1);
for (i=0;i<N;i++) printf("%lld ",c[i]);printf("\n");
fwt_and(N,a,1);fwt_and(N,b,1);
for (i=0;i<N;i++) c[i]=(a[i]*b[i])%maxd;
fwt_and(N,a,-1);fwt_and(N,b,-1);fwt_and(N,c,-1);
for (i=0;i<N;i++) printf("%lld ",c[i]);printf("\n");
fwt_xor(N,a,1);fwt_xor(N,b,1);
for (i=0;i<N;i++) c[i]=(a[i]*b[i])%maxd;
fwt_xor(N,a,-1);fwt_xor(N,b,-1);fwt_xor(N,c,-1);
for (i=0;i<N;i++) printf("%lld ",c[i]);printf("\n");
return 0;
}

FWT(快速沃尔什变换)小结的更多相关文章

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

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

  2. [学习笔记]FWT——快速沃尔什变换

    解决涉及子集配凑的卷积问题 一.介绍 1.基本用法 FWT快速沃尔什变换学习笔记 就是解决一类问题: $f[k]=\sum_{i\oplus j=k}a[i]*b[j]$ 基本思想和FFT类似. 首先 ...

  3. 浅谈算法——FWT(快速沃尔什变换)

    其实FWT我啥都不会,反正就是记一波结论,记住就好-- 具体证明的话,推荐博客:FWT快速沃尔什变换学习笔记 现有一些卷积,形如 \(C_k=\sum\limits_{i\lor j=k}A_i*B_ ...

  4. 知识点简单总结——FWT(快速沃尔什变换),FST(快速子集变换)

    知识点简单总结--FWT(快速沃尔什变换),FST(快速子集变换) 闲话 博客园的markdown也太傻逼了吧. 快速沃尔什变换 位运算卷积 形如 $ f[ i ] = \sum\limits_{ j ...

  5. 初学FWT(快速沃尔什变换) 一点心得

    FWT能解决什么 有的时候我们会遇到要求一类卷积,如下: Ci=∑j⊕k=iAj∗Bk\large C_i=\sum_{j⊕k=i}A_j*B_kCi​=j⊕k=i∑​Aj​∗Bk​此处乘号为普通乘法 ...

  6. FWT快速沃尔什变换例题

    模板题 传送门 #include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #de ...

  7. FWT快速沃尔什变换——基于朴素数学原理的卷积算法

    这是我的第一篇学习笔记,如有差错,请海涵... 目录 引子 卷积形式 算法流程 OR卷积 AND卷积 XOR卷积 模板 引子 首先,考虑这是兔子 数一数,会发现你有一只兔子,现在,我再给你一只兔子 再 ...

  8. FWT快速沃尔什变换

    前言 学多项式怎么能错过\(FWT\)呢,然而这真是个毒瘤的东西,蒟蒻就只会背公式了\(\%>\_<\%\) 或卷积 \[\begin{aligned}\\ tf(A) = (tf(A_0 ...

  9. 关于快速沃尔什变换(FWT)的一点学习和思考

    最近在学FWT,抽点时间出来把这个算法总结一下. 快速沃尔什变换(Fast Walsh-Hadamard Transform),简称FWT.是快速完成集合卷积运算的一种算法. 主要功能是求:,其中为集 ...

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

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

随机推荐

  1. foreach(Element elem in selections.Elements)无法实现

    因为版本问题 原:foreach(Element elem in selections.Elements){ } 现: ElementSet selections = new ElementSet() ...

  2. Linux高级运维 第三章 Linux基本命令操作

    3.1  Linux终端介绍.Shell提示符.Bash基本语法 3.1.1  登录LINUX终端 两种终端仿真器:1.GNOME桌面的GHOME Terminal : 2.KDE桌面的Konsole ...

  3. SpringCloud-sleuth-zipkin链路追踪

    什么是Zipkin? 它是一个分布式链路跟踪系统它可以帮助收集时间数据,每个应用程序向Zipkin报告定时数据,Zipkin UI呈现了一个依赖图表来展示多少跟踪请求经过了每个应用程序:如果想解决延迟 ...

  4. 对HTML5标签的认识(四)

    这篇随笔讲讲HTML5中的表单和表单的一些元素 一.表单的作用是什么? 概念:表单在网页中主要是负责对数据信息的采取,表单一共分成三个部分: 1.表单的标签:这里面包含了处理表单的数据所用CGI程序以 ...

  5. 06 入门 - Web服务器

    目录索引:<ASP.NET MVC 5 高级编程>学习笔记 开发和调试ASP.NET MVC程序,需要Web服务器的支持. Visual Studio 2012+开发环境提供了两种Web服 ...

  6. Ext JS中的typeOf

    Ext JS中的typeOf:以字符串格式,返回给定变量的类型 其中对字符串对象.元素节点.文本节点.空白文本节点判断并不准确 测试代码如下: <!DOCTYPE HTML PUBLIC &qu ...

  7. Javascript之高级数组API的使用实例

    JS代码中我们可以根据需求新建新的对象解决问题的同时,也有一些常用的内置对象供我们使用,我们称之为API,本篇文章只是对数组部分进行了练习. 例一:伪数组,不能修改长短的数组(所以没办法清零),可以修 ...

  8. 前端入门20-JavaScript进阶之异步回调的执行时机

    声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...

  9. 【NodeJS】基础知识

    nodejs基础 nodejs允许自己封装模块,使得编写程序可以模块化,便于维护整理.在一个js文件中写完封装的函数或对象后,可以使用exports或module.exports来将模块中的函数暴露给 ...

  10. docker根据配置文件启动redis

    更多docker基本命令请自行查询. 1.首先拉取合适版本的docker镜像 docker pull redis:5 2.如果不需要更改什么配置或者仅仅测试用可以直接启动镜像运行容器,这里要说明的是根 ...