集合并卷积与FMT

首先感谢几篇良心博客的博主,教程写得真的很好。
真正理解快速沃尔什变换/快速莫比乌斯变换(FWT|FMT) (已完结)
FMT 与 子集(逆)卷积

集合卷积

我们时常要解决一些与集合有关的卷积问题,像快速傅里叶变换那样,对下标有一些要求和限制。
FWT和FMT可以成为我们解决这类问题的有力武器。

FMT

我个人认为,FMT比FWT从原理角度上更好理解。接下来介绍一下快速莫比乌斯变换。

原理

首先我们要证明FMT可以像FFT那样乘来乘去并保证正确性。

证明

对于长度为的序列,我们定义集合并卷积,
                                                                                                                        

定义它的莫比乌斯变换为


对集合并卷积的式子两边做FMT,




至此我们就证明了其具有线性变换的性质。

正变换

直接暴力进行计算,其复杂度是O()的,这个复杂度很容易证明(虽然写烂了的话就变成O())。
一种优美的方法是分层进行递归处理,我们处理某一位i,假设一个集合S是不包含i的,那么的位置可以由S来产生贡献。
处理边界和n轮之后的变换式,就可以得到我们要的东西了。

逆变换

与正变换类似,不同点在于这次要减去SS的贡献。


这个式子用容斥证明非常容易。

Code

void FMT(int *f, int opt)  {   for (int j = 0; j < n; ++ j)   for(int i = 0; i < (1 << n); ++ i)    if (i >> j & 1)  f[i] += opt * f[i ^ (1 << j)];  } 

复杂度O(n2n)O(n2n)

其他相关

有限制的FMT

现在要求


和之前不同的地方在于多了一个二者交集为空的条件,所以我们不能无脑套用FMT了。
我们可以多加一维考虑当前集合大小为ii,集合为SS的变换值。
你问为啥多加这一维?集合SS已知还要再加一位确定大小?
其实这个ii并非真实大小,而是我们认为的大小。最后有作用的只有那些f[count(S)][S]。

for (int i = 0; i <= n; ++ i)  {  for (int j = 0; i + j <= n; ++ j)  for (int S = 0; S < (1 << n); ++ S)  h[i + j][S] += f[i][S] * g[j][S]; } 

复杂度O()。

子集逆卷积

对于要求反卷积,形如


已知h,g,求f。有


现在我们要求,即一个多项式一以下的逆。然后进行FMT就万事大吉了。

inline void Inv(int *f)  {  tmp[0] = Pow(f[0], mod - 2);  for (int i = 0; i <= n; ++ i)   {  inv[i] = tmp[i];  for (int j = 0; i + j <= n; ++ j)  tmp[i + j] -= inv[i] * f[j];  } } 

全部评论

相关推荐

腾讯 后台开发 22k+3w
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务