知识点 - 快速沃尔什变换

解决问题类型:

FWTFWTFWT是用来处理位运算(异或、与、或)卷积的一种变换。位运算卷积是什么?形如f[i]=∑j⊕k==ig[j]∗h[k]f[i]=∑_{j⊕k==i}g[j]∗h[k]f[i]=∑j⊕k==i​g[j]∗h[k]的卷积形式(其中⊕⊕⊕为位运算)就是位运算卷积。如果暴力枚举的话,时间复杂度是O(n2)O(n^2)O(n2),但运用FWTFWTFWT来解决就可达到O(nlogn)O(nlogn)O(nlogn)的时间复杂度。FSTFSTFST则是借助FWTFWTFWT来进行的对子集卷积的优化,相当于FWTFWTFWT的一个应用。

前置知识

  • 卷积
  • 异或,或,与

实现

与卷积

对于与运算,有一个结论:(i&amp;j)&amp;k==k&lt;−−&gt;(i&amp;k==k)&amp;&amp;(j&amp;k==k)(i\&amp;j)\&amp;k==k&lt;−−&gt;(i\&amp;k==k)\&amp;\&amp;(j\&amp;k==k)(i&j)&k==k<−−>(i&k==k)&&(j&k==k)。

那么我们就可以构造一个求父集和的函数 F(i)=∑j&amp;i==if(i)F(i)=\sum\limits_{j\&amp;i==i}^{ }f(i)F(i)=j&i==i∑​f(i),由此我们可以推出:

G(k)∗H(k)G(k)∗H(k)G(k)∗H(k)

=∑i&amp;k==kg(i)∑j&amp;k==kh(j)=\sum\limits_{i\&amp;k==k}^{ }g(i)\sum\limits_{j\&amp;k==k}^{ }h(j)=i&k==k∑​g(i)j&k==k∑​h(j)

=∑(i&amp;j)&amp;k==kg(i)∗h(j)=\sum\limits_{(i\&amp;j)\&amp;k==k}^{ }g(i)*h(j)=(i&j)&k==k∑​g(i)∗h(j)

=∑t&amp;k==k∑i&amp;j==tg(i)∗h(j)=\sum\limits_{t\&amp;k==k}^{ }\sum\limits_{i\&amp;j==t}^{ }g(i)*h(j)=t&k==k∑​i&j==t∑​g(i)∗h(j)

因为f(t)=∑i&amp;j==tg(i)∗h(j)f(t)=\sum\limits_{i\&amp;j==t}^{ }g(i)*h(j)f(t)=i&j==t∑​g(i)∗h(j)

所有上式=∑t&amp;k==kf(t)=F(k)=\sum\limits_{t\&amp;k==k}^{ }f(t)=F(k)=t&k==k∑​f(t)=F(k)

因此我们只需要将g,hg,hg,h正变换成G,HG,HG,H然后对应位相乘得到FFF后再将FFF逆变换回去即可得到fff。

那么如何正变换?

以下面为例:

00 a

01 b

10 c

11 d

我们从最低位开始考虑,每次只考虑只有当前位不同的两个数之间的影响。显然求父集只有111会对000有贡献,因此我们将010101的值加到000000上,将111111的值加到101010上,再看下一位,同样将111111的值加到010101上,将101010的值加到000000上。这样最后000000的值为a+b+c+da+b+c+da+b+c+d,010101的值为b+db+db+d,101010的值为c+dc+dc+d,111111的值为ddd。同样逆变换就是将111的值从000上减掉即可。

void fwt_and(int *a,int opt) {for(int k=2; k<=n; k<<=1) {for(int i=0,t=k>>1; i<n; i+=k) {for(int j=i; j<i+t; j++) {if(opt==1)  a[j]=(a[j]+a[j+t])%mod;else        a[j]=(a[j]-a[j+t]+mod)%mod;}}}
}

或卷积

或卷积和与卷积类似,对于或卷积同样有结论:(i∣j)∣k==k&lt;−−&gt;(i∣k==k)&amp;&amp;(j∣k==k)(i|j)|k==k&lt;--&gt;(i|k==k)\&amp;\&amp;(j|k==k)(i∣j)∣k==k<−−>(i∣k==k)&&(j∣k==k)这次我们需要构造一个求子集和的函数G(i)=∑j∣i==ig(j)G(i)=\sum\limits_{j|i==i}^{ }g(j)G(i)=j∣i==i∑​g(j),推导过程和与卷积类似。

对于正变换,显然只有000对111有贡献;对于逆变换,只需要将000的值从111中减掉即可。

附上代码

void fwt_or(int *a,int opt) {for(int k=2; k<=n; k<<=1) {for(int i=0,t=k>>1; i<n; i+=k) {for(int j=i; j<i+t; j++) {if(opt==1)  a[j+t]=(a[j+t]+a[j])%mod;else a[j+t]=(a[j+t]-a[j]+mod)%mod;}}}
}

异或卷积

对于异或卷积,我们设bit(i)bit(i)bit(i)代表iii的二进制中111的奇偶性,因此有一个结论(这里异或用⊕表示):

bit(i&amp;k)⊕bit(j&amp;k)=bit((i⊕j)&amp;k)bit(i\&amp;k)\oplus bit(j\&amp;k)=bit((i\oplus j)\&amp;k)bit(i&k)⊕bit(j&k)=bit((i⊕j)&k)

对于原多项式ggg构造G(i)=∑j=02n−1(−1)bit(j&amp;i)g(j)G(i)=\sum\limits_{j=0}^{2^n-1}(-1)^{bit(j\&amp;i)}g(j)G(i)=j=0∑2n−1​(−1)bit(j&i)g(j)

开始推导:
KaTeX parse error: No such environment: align at position 17: …{align} \begin {̲a̲l̲i̲g̲n̲}̲ G(k)*H(k) & \\… {align}
对于正变换,我们同样从最低位向最高位考虑,每次只考虑只有当前位不同的两个数之间的影响。对于每对000和111(设值分别为aaa和bbb),0&amp;0=00\&amp;0=00&0=0和0&amp;1=00\&amp;1=00&1=0都不会影响bitbitbit的值,所以000那个位置的值变成a+ba+ba+b;1&amp;0=01\&amp;0=01&0=0不会影响bitbitbit的值,但1&amp;1=11\&amp;1=11&1=1会影响bitbitbit的值(相当于在前面乘上一个−1−1−1的系数),因此111那个位置的值变成a−ba−ba−b。对于逆变换,相当于我们现在知道两个位置x=a+bx=a+bx=a+b,y=a−by=a−by=a−b,求aaa和bbb,可以得到a=(x+y)/2,b=(x−y)/2a=(x+y)/2,b=(x−y)/2a=(x+y)/2,b=(x−y)/2。

void fwt_xor(int *a,int opt) {int tmp;for(int k=2; k<=n; k<<=1) {for(int i=0,t=k>>1; i<n; i+=k) {for(int j=i; j<i+t; j++) {tmp=a[j];a[j]=(a[j]+a[j+t])%mod;a[j+t]=(tmp-a[j+t]+mod)%mod;if(opt==-1) {a[j]=1ll*a[j]*inv%mod;a[j+t]=1ll*a[j+t]*inv%mod;}}}}
}

K进制异或卷积

可以发现二进制的异或运算相当于不进位加法即每一位对应相加后对222取模,而与运算相当于不进位乘法即每一位对应相乘后对222取模,bitbitbit相当于求二进制每一位的和对222取模。那么我们将这些在二进制下的运算扩展到KKK进制可以发现同样满足上述的结论,但对于从ggg求GGG的部分每个数前面的系数的底数是−1−1−1,显然这个系数不能扩展到KKK进制。那么我们现在就需要找一个系数www满足w0,w1,w2……wk−1w0,w1,w2……wk−1w0,w1,w2……wk−1都各不相同且wi=wiwi=wi%kwi=wi。从FFTFFTFFT中我们知道了复数单位根这个东西,那么我们完全可以将www取KKK次单位根,这样就可以满足以上性质了!类比二进制亦或的正变换也可以得出KKK进制的正变换。

FST

FSTFSTFST通常用来优化一类子集DPDPDP,例如f(S)=∑g(T)∗h(S−T)f(S)=∑g(T)∗h(S−T)f(S)=∑g(T)∗h(S−T),其中TTT是SSS的子集。

这里就不在介绍了。

复杂度

O(NlogN)O(NlogN)O(NlogN)

例题

  1. bzoj4589: Hard Nim

  2. H 牛客

  3. Tree Cutting HDU - 5909

  4. 622C Binary Table.cpp状态压缩 、FWT

  5. Maxor 求一组数中,两两异或的最大值,并输出方案数

代码

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_backtypedef long long ll;
typedef pair<int,int> pi;
const int MAXN = (int)1e6+7;const int MOD=998244353,inv2=(MOD+1)/2;
int a[1<<17],b[1<<17];
int f[1<<17],g[1<<17];
void fwtor(int f[],int n,int op){for(int p=2;p<=n;p<<=1){int len=p>>1;for(int k=0;k<n;k+=p)for(int i=k;i<k+len;++i)f[i+len]=(f[i+len]+op*f[i])%MOD;}
}
void calcor(int f[],int g[],int n){fwtor(f,n,1),fwtor(g,n,1);for(int i=0;i<n;++i) f[i]=(ll)f[i]*g[i]%MOD;fwtor(f,n,-1);for(int i=0;i<n;++i) f[i]=(f[i]+MOD)%MOD;
}void fwtand(int f[],int n,int op){for(int p=2;p<=n;p<<=1){int len=p>>1;for(int k=0;k<n;k+=p)for(int i=k;i<k+len;++i)f[i]=(f[i]+op*f[i+len])%MOD;}
}
void calcand(int f[],int g[],int n){fwtand(f,n,1),fwtand(g,n,1);for(int i=0;i<n;++i) f[i]=(ll)f[i]*g[i]%MOD;fwtand(f,n,-1);for(int i=0;i<n;++i) f[i]=(f[i]+MOD)%MOD;
}void fwtxor(int f[],int n,int op){for(int p=2;p<=n;p<<=1){int len=p>>1;for(int k=0;k<n;k+=p)for(int i=k;i<k+len;++i){int t=f[i+len];f[i+len]=(f[i]-f[i+len]+MOD)%MOD;f[i]=(f[i]+t)%MOD;}if(op==-1) for(int i=0;i<n;++i) f[i]=(ll)f[i]*inv2%MOD;}
}
void calcxor(int f[],int g[],int n){fwtxor(f,n,1),fwtxor(g,n,1);for(int i=0;i<n;++i) f[i]=(ll)f[i]*g[i]%MOD;fwtxor(f,n,-1);
}int main()
{int n;ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n;n = (1<<n); //注意这里传入的n必须是2的倍数,注意f,g数组的初始化必须也要到n为止。rep(i,0,n-1) cin >> a[i];rep(i,0,n-1) cin >> b[i];for(int i=0;i<n;++i) f[i]=a[i],g[i]=b[i];calcor(f,g,n);rep(i,0,n-1) cout << f[i] << " ";cout << endl;for(int i=0;i<n;++i) f[i]=a[i],g[i]=b[i];calcand(f,g,n);rep(i,0,n-1) cout << f[i] << " ";cout << endl;for(int i=0;i<n;++i) f[i]=a[i],g[i]=b[i];calcxor(f,g,n);rep(i,0,n-1) cout << f[i] << " ";cout << endl;
}

知识点 - 快速沃尔什变换相关推荐

  1. 解题报告(一)快速沃尔什变换FWT(ACM / OI)超高质量题解

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...

  2. 《小学生都能看懂的快速沃尔什变换从入门到升天教程》(FWT / FMT / FMI)(最最严谨清晰的证明!零基础也能得学会!)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 0x00 卷积 0x01 多项式 0x02 卷积的定义 0x03 卷积的基本性质 0x04 位运 ...

  3. Fast Walsh-Hadamard Transform——快速沃尔什变换

    模板题: 给定$n = 2^k$和两个序列$A_{0..n-1}$, $B_{0..n-1}$,求 $$C_i = \sum_{j \oplus k = i} A_j B_k$$ 其中$\oplus$ ...

  4. 快速沃尔什变换(FWT)

    1前言 在之前学完了FFT稍微码了一些题.也学习了一下NTT相关的知识之后,我觉得有必要学习一下FWT,这篇博客就是阐述我对FWT的理解的 2介绍 2.1解决的问题 对于FFT,它的过程本质上是cn= ...

  5. 模板:快速莫比乌斯变换(FMT)+快速沃尔什变换(FWT)(多项式)

    文章目录 前言 解析 OR 定义 变换: 逆变换 代码 AND 代码 XOR 定义 变换 逆变换 代码 所谓快速沃尔什变换,就是快速的沃尔玛什锦专柜变换 (逃) 前言 正常卷积的定义:ck=∑i+j= ...

  6. Java虚拟机知识点快速复习手册(上)

    前言 本文快速回顾了常考的的知识点,用作面试复习,事半功倍. 上篇主要内容为:虚拟机数据区域,垃圾回收 下篇主要内容为:类加载机制 面试知识点复习手册 全复习手册文章导航 Csdn全复习手册文章导航: ...

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

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

  8. 计算机网络基础知识点快速复习手册

    前言 本文快速回顾了计算机网络书本中常考的的知识点,用作面试复习,事半功倍. 主要内容有:计算机网络体系结构,TCP与UDP,UDP/TCP实现DEMO代码 面试知识点复习手册 全复习手册文章导航 全 ...

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

    前言 首先,我们来看看多项式乘法. f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n f(x)=a_0+a_1x+a_2x^2+\dots+a_nx^n f( ...

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

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

最新文章

  1. 《UNIX环境高级编程(第3版)》——1.7 出错处理
  2. Habitica 4.85.5 发布,习惯游戏养成应用
  3. 搭建私有Git服务器
  4. 限制域用户多点登录--脚本
  5. 不定位成一个连接者,家装公司进军智能装饰的所有姿势都是错的
  6. 【windows环境——VSCode安装教程】
  7. python普通类实现接口_python之面向对象(接口和抽象类)
  8. linux 进程 异常日志,linux后台启动程序,处理nohup日志太大的问题
  9. python字符编码转换说明及深浅copy介绍
  10. Shiro(三)——Shiro授权入门案例
  11. csdn积分如何获取攻略
  12. 台型计算机电源电路图,电脑ATX电源控制电路及原理
  13. [web开发] Vue+Spring Boot 上海大学预约系统开发记录
  14. python 排列组合 数据量过大_Python 排列组合的实现
  15. 华为认证云服务工程师(HCIA-Cloud ServiceV3.0)-- 认证介绍
  16. C语言怎样提取一个数的十位个位百位千位
  17. 易语言 网页_取文本_reg的源码
  18. 简单解释一下一个项目中的pojo模块
  19. UI网页设计制作思路
  20. 机器学习需要掌握的数学知识点---详细整理

热门文章

  1. BT5 CDLinux+U盘启动 破解无线网络
  2. 披着“云”衣裳的狗——搜狗输入法“云”版本尝鲜记
  3. Mysql数据库——高阶语句(上)
  4. dbcp 出现的connection is closed的问题
  5. 布线可视化管理12芯OM3室内多模光缆
  6. 可能是讲分布式系统最到位的一篇文章
  7. layuiadmin配置mysql_layuiAdmin 后台管理模板
  8. 阿里云部署nginx
  9. tif文件批量转png
  10. pythonrq模块_Python RQ 任务队列中的队列 ( Queue )