快速沃尔什变换小记
概述
\(FWT\)是用来处理集合卷积的问题。也就是求解\(f(n)\sum\limits_{i|j=n}f(i)f(j)\)类型的问题。其中或运算可以改为\(\otimes,\&\)。
寻找点值
因为总是看不下去那么长的推导,所以每次都是看到一半。然后就在加上自己的一点理解,简单推导一下吧(背过结论就行)
以或运算为例。为什么说是集合卷积呢。因为或运算等价于求集合并。也就是求\(f(n)\sum\limits_{i\cup j=n}f_1(i)f_2(j)\) 。
那么我们类似于\(FFT\),先将他转化为点值,然后进行乘法运算后,在转换回来。
如何转化为点值呢。或者说他的点值长什么样子呢。
我们求一个\(g(n)=\sum\limits_{s\subseteq n}f(n)\)。然后我们求一下\(g(n)\)的乘积。
我们令\(g_1\)表示\(f_1\)转化后的结果,\(g_2\)表示\(f_2\)转化或的结果,\(g\)表示卷积\(f\)转化后的结果。
也就是\(g_1(n)g_2(n)=\sum\limits_{s_1\subseteq n}f_1(s_1)\sum\limits_{s_2\subseteq n}f_2(s_2)=\sum\limits_{s_1,s2\subseteq n}f_1(s_1)f_2(s_2)=\sum\limits_{s_1\cup s_2\subseteq n}f_1(s_1)f_2(s_2)=g(n)\)
所以\(g(n)=\sum\limits_{s\subseteq n}f(n)\)就是我们要的点值表达式!
相互转化
有了点值之后我们还需要在点值与多项式之间相互转化,那么应该怎么转化呢。
其实很简单,观察\(g(n)=\sum\limits_{s\subseteq n}f(n)\)。这个式子,其实就是一个高维前缀和嘛。。
然后转化回去同样的来个高维差分就ok了。
高维前缀和代码如下:
void fwt_or(int *a,int xs) {
for(int i = 0;i < n;++i)
for(int j = 0;j < (1 << n);++j)
if(!((j >> i) & 1))
a[j | (1 << i)] += a[j];
}
对于另外两种运算
对于\(\otimes\)和\(\&\)。与\(|\)类似,也有不同之处。因为\(\&\)表示的是集合交,所以他是枚举超集和而不是子集和。
至于\(\otimes\),背板子吧我也不会推导啊qwq
板子
板子里面,\(xs=1\)时表示\(FWT\),即将多项式转化为点值。\(xs=-1\)时表示\(IFWT\),即将点值转化回多项式。
或运算
void fwt_or(int *a,int xs) {
for(int i = 0;i < n;++i)
for(int j = 0;j < (1 << n);++j)
if(!((j >> i) & 1))
a[j | (1 << i)] += xs * a[j];
}
and运算
void fwt_and(int *a,int xs) {
for(int i = 0;i < n;++i)
for(int j = 0;j < (1 << n);++j)
if(!((j >> i) & 1))
a[j] += xs * a[j | (1 << i)];
}
异或运算
void fwt_xor(int *a,int xs) {
for(int i = 0;i < n;++i) {
for(int j = 0;j < (1 << n);++j) {
if(!((j >> i) & 1)) {
int l = a[j],r = a[j | (1 << i)];
a[j] = l + r;a[j] %= mod;
a[j | (1 << i)] = l - r;a[j | (1 << i)] %= mod;
}
}
}
if(xs == -1) {
int inv = qm(1 << n,mod - 2);
for(int i = 0;i < (1 << n);++i)
a[i] = 1ll * a[i] * inv % mod;
}
}
小技巧
在很多题目中,需要进行多次\(FWT\)运算,我们不需要每次将数组来回转化,只要先将多项式转化为点值,然后对点值进行快速幂运算,最后在转化回去就行。
模板题
/*
* @Author: wxyww
* @Date: 2020-04-26 08:03:27
* @Last Modified time: 2020-04-26 08:43:59
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 1 << 20,mod = 998244353;
ll read() {
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1; c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar();
}
return x * f;
}
int A[N],B[N],n;
void fwt_and(int *a,int xs) {
for(int i = 0;i < n;++i) {
for(int j = 0;j < (1 << n);++j) {
if(!((j >> i) & 1)) {
a[j] += xs * a[j | (1 << i)];
a[j] %= mod;
}
}
}
}
void fwt_or(int *a,int xs) {
for(int i = 0;i < n;++i) {
for(int j = 0;j < (1 << n);++j) {
if(!((j >> i) & 1)) {
a[j | (1 << i)] += xs * a[j];
a[j | (1 << i)] %= mod;
}
}
}
}
ll qm(ll x,ll y) {
ll ret = 1;
for(;y;y >>= 1,x = x * x % mod)
if(y & 1) ret = ret * x % mod;
return ret;
}
void fwt_xor(int *a,int xs) {
for(int i = 0;i < n;++i) {
for(int j = 0;j < (1 << n);++j) {
if(!((j >> i) & 1)) {
int l = a[j],r = a[j | (1 << i)];
a[j] = l + r;a[j] %= mod;
a[j | (1 << i)] = l - r;a[j | (1 << i)] %= mod;
}
}
}
if(xs == -1) {
int inv = qm(1 << n,mod - 2);
for(int i = 0;i < (1 << n);++i) {
a[i] = 1ll * a[i] * inv % mod;
}
}
}
int tmp1[N],tmp2[N];
int main() {
n = read();
for(int i = 0;i < (1 << n);++i) A[i] = read();
for(int i = 0;i < (1 << n);++i) B[i] = read();
memcpy(tmp1,A,sizeof(tmp1));
memcpy(tmp2,B,sizeof(tmp2));
fwt_or(tmp1,1);fwt_or(tmp2,1);
for(int i = 0;i < (1 << n);++i) tmp1[i] = 1ll * tmp1[i] * tmp2[i] % mod;
fwt_or(tmp1,-1);
for(int i = 0;i < (1 << n);++i) printf("%d ",(tmp1[i] + mod) % mod);puts("");
memcpy(tmp1,A,sizeof(tmp1));
memcpy(tmp2,B,sizeof(tmp2));
fwt_and(tmp1,1);fwt_and(tmp2,1);
for(int i = 0;i < (1 << n);++i) tmp1[i] = 1ll * tmp1[i] * tmp2[i] % mod;
fwt_and(tmp1,-1);
for(int i = 0;i < (1 << n);++i) printf("%d ",(tmp1[i] + mod) % mod);puts("");
memcpy(tmp1,A,sizeof(tmp1));
memcpy(tmp2,B,sizeof(tmp2));
fwt_xor(tmp1,1);fwt_xor(tmp2,1);
for(int i = 0;i < (1 << n);++i) tmp1[i] = 1ll * tmp1[i] * tmp2[i] % mod;
fwt_xor(tmp1,-1);
for(int i = 0;i < (1 << n);++i) printf("%d ",(tmp1[i] + mod) % mod);puts("");
return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/fwt.html
- 2014密码时代已死?六种旨在取代传统密码位置的新奇想法
- 程序员你为什么这么累【续】:编码习惯之配置规范
- Spring Security (一) Architecture Overview
- Spring Security (二) Guides
- 一个 android 的框架
- Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式
- android常用接口(二)
- Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用
- RxAndroid完全教程
- 全能型反汇编引擎 – Capstone-Engine
- Hijack攻击揭秘
- 都在说微服务,那么微服务的反模式和陷阱是什么(二)
- Spring Boot 2.0 - WebFlux framework
- Spring Cloud构建微服务架构:服务网关(路由配置)【Dalston版】
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- requests项目实战--抓取百度热搜
- [代码片段]谷歌的一个不错的CSS样式,不得不说大厂的前端设计师美感就是好。
- 罗马数字转整数
- 找出两个文件中相同的单词(java实现)
- Apache日志变量详解
- 三、jQuery中的Ajax
- SAP ABAP ADBC和Java JDBC的使用比较
- 自己开发的一个SAP CRM订单统计工具
- SAP ABAP和Java跨域请求问题的解决方案
- 使用ABAP Channel实现一个订单跟踪工具,提高日常工作效率
- SAP CRM状态字段下拉列表里数据的填充原理
- SAP CRM订单状态管理的一些重要的数据库表
- Angular应用的依赖注入调试
- 给Angular应用增添搜索Search功能
- Angular应用一个创建场景的问题分析