FWT,FST入门
0.目录
1.什么是 FWT
FWT 全称为 " 快速沃尔什变换: Fast Walsh Transform " 。可以用于解决位运算卷积的问题。
什么叫位运算卷积呢?我们考虑普通的卷积,即:
\]
位运算卷积就是下标为位运算的卷积(此处与和或用 C++ 记号,异或用\(\oplus\)):
\]
2. FWT 怎么做
为了方便,以下我们假设所有向量长度都相等,为\(2\)的整幂,即长度\(n=2^m\)。高位以 0 补齐。
设一个向量\(A\)经过 FWT 之后得到了\(FWT(A)\), FWT 的最终目标就是满足:\(FWT(C)=FWT(A)\cdot FWT(B)\),其中的点乘表示向量的每一位相乘:\(A\cdot B=(A_0B_0,A_1B_1,...,A_iB_i,...)\)。
FWT 针对三种位运算有各自的处理方法:
2.1. 或卷积
我们发现或运算存在如下的性质:
\]
如果将二进制理解为一个 01 集合,我们就可以用集合并的方式理解上面的性质。发现这其实是一个伪分配律。
根据这个性质我们可以进行构造:
\]
那么可以发现:
a_ib_i
&=\sum_{j|i=i}A_j\times \sum_{k|i=i}B_k\\
&=\sum_{j|i=i}\sum_{k|i=i}A_jB_k\\
&=\sum_{(j|k)|i=i}A_jB_k\\
&=c_i
\end{aligned}
\]
这样做了对应乘法之后就可以得到\(c\),再进行一次逆变换就可以得到\(C\)。
因此问题变成了怎么进行这样的变换。
考虑一种分治(或者称为 DP )的做法:
\(f(i,j)\):满足仅有低\(i\)位可能与\(j\)不同的,且与\(j\)或后得到\(j\)的下标所对应的数的和。
或者可以被描述为:
\]
考虑如何进行转移,即从\(f(i-1)\)转移到\(f(i)\)。这种情况下只有第\(i\)位解除了限制。根据或的性质,如果第\(i\)位为 0 ,那么第\(i\)位或操作之后仍需要是 0 ,就只能从第\(i\)位为 0 的\(f(i,j)\)转移来;如果第\(i\)位为 1 ,那么第\(i\)位或操作总得到是 1 ,就可以不考虑第\(i\)位,从\(f(i,j)\)和\(f(i,j+2^i)\)转移来。
放个图片理解一下:
顺便可以得到转移为:
正变换:
f(i,j)&=f(i-1,j)\\
f(i,j+2^i)&=f(i-1,j)+f(i-1,j+2^i)
\end{aligned}
\]
逆变换就是将特殊贡献扣除:
f(i,j)&=f(i+1,j)\\
f(i,j+2^i)&=f(i+1,j+2^i)-f(i+1,j)
\end{aligned}
\]
可以发现\(f(0,i)=A_i\),且这个转移可以滚动数组优化。
这样的 FWT 就是\(O(n\log_2n)\)。
2.2.与卷积
与卷积与或卷积十分相似,因此可以用类似的方法分析。这里只给出状态和转移:
\]
正变换:
f(i,j)&=f(i-1,j)+f(i-1,j+2^i)\\
f(i,j+2^i)&=f(i-1,j+2^i)
\end{aligned}
\]
逆变换:
f(i,j)&=f(i+1,j)-f(i+1,j+2^i)\\
f(i,j+2^i)&=f(i+1,j+2^i)
\end{aligned}
\]
2.3.异或卷积
我们同样考虑异或的性质。
设\(count(i)\)为\(i\)的二进制中\(1\)的位数,\(i\otimes j=count(i\&j)\bmod 2\)则异或有性质:
\]
即奇偶性相等。设\(count(j\&i)=a,count(k\&i)=b,count(j\&k\&i)=c\),则左侧奇偶性由\(a+b\)决定,右侧奇偶性由\(a+b-2c\)决定,可以发现两侧的奇偶性相等。
说起奇偶性,我们可以想到\(-1\)的幂。于是设:
a_i=\sum_j(-1)^{i\otimes j}A_j\\
b_i=\sum_j(-1)^{i\otimes j}B_j\\
c_i=\sum_j(-1)^{i\otimes j}C_j
\end{aligned}
\]
然后就可以看看这样转换后乘起来的结果:
a_ib_i
&=\sum_j(-1)^{i\otimes j}A_j\times \sum_k(-1)^{i\otimes k}B_k\\
&=\sum_{i\otimes j=0}\sum_{i\otimes k=0}A_jB_k-\sum_{i\otimes j=1}\sum_{i\otimes k=0}A_jB_k-\sum_{i\otimes j=0}\sum_{i\otimes k=1}A_jB_k+\sum_{i\otimes j=1}\sum_{i\otimes k=1}A_jB_k\\
&=\sum_{i\otimes (j\oplus k)=0}A_jB_k-\sum_{i\otimes (j\oplus k)=1}A_jB_k\\
&=\sum_p(-1)^{i\otimes p}\sum_{j\oplus k=p}A_jB_k\\
&=\sum_p(-1)^{i\otimes p}C_p\\
&=c_i
\end{aligned}
\]
我们达成了目的。接下来就看看怎么变换。继续考虑 DP :
\]
可以发现,从\(f(i-1)\)转到\(f(i)\)的时候,只有第\(i\)位都是\(1\)才会令\(j\otimes k\)改变奇偶性,即多乘上一个 -1 。
这样转移,最终\(j\otimes k=0\)的情况就会被乘上偶数次 -1 ,而\(j\otimes k=1\)的情况就会被乘上奇数次 -1 ,最终答案就是正确的。
按照这样,正变换:
f(i,j)&=f(i-1,j)+f(i-1,j+2^i)\\
f(i,j+2^i)&=f(i-1,j)-f(i-1,j+2^i)
\end{aligned}
\]
逆变换,用到了小学奥数的 " 和差问题 " 的结论:
f(i,j)&=\frac{f(i+1,j)+f(i+1,j+2^i)} 2\\
f(i,j+2^i)&=\frac{f(i+1,j)-f(i+1,j+2^i)} 2
\end{aligned}
\]
需要注意的是,异或卷积的逆变换还有一个 " 类 FFT " 的写法,即逆变换只比正变换在最后多除一个\(n\)(事实上异或 FWT 和 FFT 有很多相似处,可以在 K 进制 FWT 中找到答案)。
2.4.例题
洛谷P4717。三种 FWT 全家桶。
参考代码:
const int mod = 998244353, inv2 = 499122177;
const int MAXSIZ = 5e5 + 5;
int A[MAXSIZ], B[MAXSIZ], C[MAXSIZ], a[MAXSIZ], b[MAXSIZ];
int N, M;
int fix( const int x ) { return ( x % mod + mod ) % mod; }
namespace OR
{
void FWT( int *f, const int m )
{
for( int s = 2 ; s <= M ; s <<= 1 )
for( int i = 0, t = s >> 1 ; i < M ; i += s )
for( int j = i ; j < i + t ; j ++ )
f[j + t] = fix( f[j + t] + f[j] * m ) % mod;
}
}
namespace AND
{
void FWT( int *f, const int m )
{
for( int s = 2 ; s <= M ; s <<= 1 )
for( int i = 0, t = s >> 1 ; i < M ; i += s )
for( int j = i ; j < i + t ; j ++ )
f[j] = fix( f[j] + f[j + t] * m ) % mod;
}
}
namespace XOR
{
void FWT( int *f, const int m )
{
int t1, t2;
for( int s = 2 ; s <= M ; s <<= 1 )
for( int i = 0, t = s >> 1 ; i < M ; i += s )
for( int j = i ; j < i + t ; j ++ )
{
t1 = f[j], t2 = f[j + t];
if( m > 0 )
f[j] = ( t1 + t2 ) % mod,
f[j + t] = fix( t1 - t2 );
else
f[j] = 1ll * ( t1 + t2 ) % mod * inv2 % mod,
f[j + t] = 1ll * fix( t1 - t2 ) * inv2 % mod;
}
}
}
void cal( void ( *fwt ) ( int*, int ) ) //函数指针的写法,主要是方便。
{
for( int i = 0 ; i < M ; i ++ ) A[i] = a[i], B[i] = b[i];
fwt( A, 1 ), fwt( B, 1 );
for( int i = 0 ; i < M ; i ++ ) C[i] = 1ll * A[i] * B[i] % mod;
fwt( C, -1 );
for( int i = 0 ; i < M ; i ++ ) write( C[i] ), putchar( i == M - 1 ? '\n' : ' ' );
}
3. FST
3.1. FST 怎么做
它听着不妙。
快速子集变换 FST 解决的是一类子集卷积的问题,即:
\]
这个卷积和或卷积的区别在于,或卷积可以有交集(并不要求\(j\& k=0\)),然而子集卷积不可以有。
注意到这个限制在子集卷积中等价于\(|S|+|T|=|U|\)。因此我们可以给状态加上一维限制大小:
\(f(i,U)\):大小为\(i\)的集合\(U\)的所有子集的贡献,\(g\)和\(h\)同理转换。
这个信息可以直接用 FWT 正变换得到。
因此有转移:
\]
转移完成后需要 FWT 逆变换回来,再将不符合要求(集合大小不匹配)的清除。
3.2.例题
[CF914G]Sum The Fibonacci, FWT + FST ,附赠题解一篇。
FWT,FST入门的更多相关文章
- 【codeforces914G】Sum the Fibonacci FWT+FST(快速子集变换)
题目描述 给出一个长度为 $n$ 的序列 $\{s\}$ ,对于所有满足以下条件的五元组 $(a,b,c,d,e)$ : $1\le a,b,c,d,e\le n$ : $(s_a|s_b)\& ...
- 快速沃尔什变换(FWT)及K进制异或卷积&快速子集变换(FST)讲解
前言: $FWT$是用来处理位运算(异或.与.或)卷积的一种变换.位运算卷积是什么?形如$f[i]=\sum\limits_{j\oplus k==i}^{ }g[j]*h[k]$的卷积形式(其中$\ ...
- P4221 [WC2018]州区划分 无向图欧拉回路 FST FWT
LINK:州区划分 把题目中四个条件进行规约 容易想到不合法当前仅当当前状态是一个无向图欧拉回路. 充要条件有两个 联通 每个点度数为偶数. 预处理出所有状态. 然后设\(f_i\)表示组成情况为i的 ...
- luogu P6097 子集卷积 FST FWT
LINK:子集卷积 学了1h多 终于看懂是怎么回事了(题解写的不太清楚 翻了好几篇博客才懂 一个需要用到的性质 二进制位为1个数是i的二进制数s 任意两个没有子集关系.挺显然. 而FST就是利用这个性 ...
- 知识点简单总结——FWT(快速沃尔什变换),FST(快速子集变换)
知识点简单总结--FWT(快速沃尔什变换),FST(快速子集变换) 闲话 博客园的markdown也太傻逼了吧. 快速沃尔什变换 位运算卷积 形如 $ f[ i ] = \sum\limits_{ j ...
- CF914G Sum the Fibonacci(FWT,FST)
CF914G Sum the Fibonacci(FWT,FST) Luogu 题解时间 一堆FWT和FST缝合而来的丑陋产物. 对 $ cnt[s_{a}] $ 和 $ cnt[s_{b}] $ 求 ...
- [WC2018]州区划分(FWT,FST)
[WC2018]州区划分(FWT,FST) Luogu loj 题解时间 经典FST. 在此之前似乎用到FST的题并不多? 首先预处理一个子集是不是欧拉回路很简单,判断是否连通且度数均为偶数即可. 考 ...
- [WC2018]州区划分——FWT+DP+FST
题目链接: [WC2018]州区划分 题目大意:给n个点的一个无向图,点有点权,要求将这n个点划分成若干个部分,每部分合法当且仅当这部分中所有点之间的边不能构成欧拉回路.对于一种划分方案,第i个部分的 ...
- FWT 入门
#include <bits/stdc++.h> using namespace std; #define ll long long const ll maxn = 3e5+5; cons ...
随机推荐
- Objective-C中的加号与减号
在Objective-C中,方法分为类方法和实例方法. 前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例.有人把它称为创建实例的工厂方法. 前置减号(-)的方 ...
- jQuery根据元素值删除数组元素的方法
http://www.jb51.net/article/68349.htm 本文实例讲述了jQuery根据元素值删除数组元素的方法.分享给大家供大家参考.具体如下: 例如删除C这个元素 ,前提不知道C ...
- SSL F5
应用交付领域经常提到SSL加速,但SSL加速到底是什么意思?SSL加速和F5指什么意思呢?在网上查询和整理了一些关于SSL加速和F5的相关解释,仅供参考:SSL加速:加密套接层协议(简称SSL)是网络 ...
- 【Java_集合框架Set】HashSet、LinkedHashSet、TreeSet使用区别
HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放: LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代: Tree ...
- Docker搭建VS Code Server ,设置访问密码随时随地写代码
今天在N1盒子上安装了 VS Code Server,简单的记录一下. 安装docker Docker一键安装脚本 $ sudo wget -qO- https://get.docker.com/ | ...
- zookeeper实现分布式锁总结,看这一篇足矣(设计模式应用实战)
分布式锁纵观网络各种各样的帖子层出不穷,笔者查阅很多资料发现一个问题,有些文章只写原理并没有具体实现,有些文章虽然写了实现但是并不全面 借这个周末给大家做一个总结,代码拿来就可以用并且每一种实现都经过 ...
- 机器学习pdf资源
<深度学习>(Deep Learning)中文版pdf, 百度网盘: https://pan.baidu.com/s/1jHDiQTg <机器学习导论>(Introductio ...
- Mybatis执行器源码手记
今天将Mybatis的执行器部分做一下简单手记. 一.java原生JDBC 众所周知,Mybatis是一个半自动化ORM框架.其实说白了,就是将java的rt.jar的JDBC操作进行了适度的封装.所 ...
- Magicodes.IE 在100万数据量下导入导出性能测试
原文作者:HueiFeng 前言 目前Magicodes.IE更新到了2.2.3,感谢大家的支持,同时建议大家在使用过程中如果遇到一些问题或者说需要一些额外的功能可以直接提issues,当然更建议大家 ...
- Vue中导出Excel表格方法
本文记录一下在Vue中实现导出Excel表格的做法.参考度娘上各篇博客,最后实现功能 Excel表格,我的后端返回的是数据流,然后文件名是放进了content-disposition中,前端进行获取. ...