快速莫比乌斯变换(FMT)与卷积
快速莫比乌斯变换(FMT)
莫比乌斯变换及反演(子集变换及反演)
设 $ f,g $ 为定义在全集 $ U $ 的任意子集上的函数,如果有莫比乌斯变换:
那么就有广义莫比乌斯反演(简称莫比乌斯反演):
证明:
考虑二项式定理推论式:
考虑对于大小为 $ n $ 的集合做如上子集操作,有:
因此对原等式左侧进行恒等变换,有:
不妨令 $ T =S \setminus P $ ,显然存在 $ T \subseteq S ,|T|=|S|-|P| $ 。对上式做等效变换得:
证毕。
合并卷积
我们定义定义域在集合 $ S $ 的全体子集上的函数 $ h(S),f(S),g(S) $ ,若称 $ h(S) $ 为 $ f(S),g(S) $ 的合并卷积,则有:
不妨设 $ \hat{h}(S) $ 为 $ h(S) $ 的莫比乌斯变换,则有:
上述推导最后的结果启发我们在对 $ f,g $ 进行合并卷积的时候,可以先将两者进行莫比乌斯变换,相乘之后得到目标函数 $ h $ 的莫比乌斯变换 $ \hat{h} $ ,再对 $ \hat{h} $ 做莫比乌斯反演,即可以得到合并卷积结果 $ h $ 。
快速莫比乌斯变换(Fast Möbius Transform , FMT)
现在我们聚焦于如何快速的计算一个定义域为集合 $ S $ 的函数 $ f(S) $ 的莫比乌斯变换 $ \hat{f}(S) $ ,即:
我们将集合 $ S $ 中的元素依次编号为 $ 1 $ 至 $ n $ ,方便用二进制表示子集状态。
定义 $ \hat{f}(S,i)=\sum\limits_{T \subseteq S}f(T)[ {i+1,i+2,\ldots,n}\subseteq T] $ 表示状态为集合 $ {i+1,i+2,\dots,n } $ 一定含于 $ T $ 且一定没有选择 $ i $ ,而 $ \hat{f}(S \cup {i},i-1) $表示 $ {i+1,i+2,\ldots,n} $ 一定含于 $ T $ 并且一定选了 $ i $。显然有 $ \hat{f}(S,0)=f(S),\hat{f}(S,|S|)=f(\phi) $ ,并且有转移方程:
其实就是高维前缀和,时间复杂度为 $ O(n\times2^n) $。
至于逆变换,只需要将加号变为减号即可。
void FMT(int *f,int type)//莫比乌斯变换type=1,莫比乌斯反演type=-1
{
for(int i=0;i<n;++i)
for(int s=0;s<(1<<n);++s)
if(s&(1<<i)) f[s]+=type*f[s^(1<<i)];
}
子集卷积
题目:[P6097 【模板】子集卷积 - 洛谷](https://www.luogu.com.cn/problem/P6097)
题意:
给定 $ n $ ,以及给定两个长度为 $ 2^n $ 的序列 $ a_0,a_1,\ldots,a_{2^n-1} $ 和 $ b_0,b_1,\ldots,b_{2^n-1} $ ,求出两者的子集卷积结果 $ c_0,c_1,\ldots,c_{2^n-1} $ ,其中:
我们定义定义域在集合 $ S $ 的全体子集上的函数 $ h(S),f(S),g(S) $ ,若称 $ h(S) $ 为 $ f(S),g(S) $ 的子集卷积,则有:
对比之前的合并卷积,发现子集卷积多了一个 $ A \cap B=\phi $ 的要求。
我们设 $ p(x) $ 为 $ x $ 的 $ popcount $(即 $ 1 $ 的个数),那么:
我们用状压状态来代表一个集合,不妨令 $ A $ 的状压状态为 $ i $ ,$ B $ 的状压状态为 $ j $ ,全集为 $ s $。那么则有:
有转移:
将状态拓展到二维,设 $ h_{x,s} $ 为状压状态为 $ s $ ,其中有 $ x $ 个元素,显然只有 $ h_{p(s),s} $ 时状态才有意义,那么可以得到卷积:
同时我们也要将 $ f(i),g(j) $ 拓展到二维,不妨令:
那么可以得到卷积:
我们暴力枚举 $ x,p(i) $ ,这样就可以唯一确定 $ p(j) $ ,然后我们再正常进行点乘操作,最后进行一次莫比乌斯反演即可,时间复杂度 $ O(n^2 \times 2^n) $ 。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
#define bitcnt(x) __builtin_popcount(x)
const int mod=1e9+9;
int A[1<<21],B[1<<21];
int a[21][1<<21],b[21][1<<21],c[21][1<<21];
int n,len;
void FMT(int *f,int n)
{
for(int i=0;i<n;++i)
for(int j=0;j<(1<<n);++j)
if(j&(1<<i)) f[j]=(f[j]+f[j^(1<<i)])%mod;
}
void IFMT(int *f,int n)
{
for(int i=0;i<n;++i)
for(int j=0;j<(1<<n);++j)
if(j&(1<<i)) f[j]=((f[j]-f[j^(1<<i)])%mod+mod)%mod;
}
void Convolution(int n)
{
for(int x=0;x<=n;++x)
{
for(int i=0;i<=x;++i)
for(int s=0;s<(1<<n);++s)
c[x][s]=(1LL*c[x][s]+1LL*a[i][s]*b[x-i][s]%mod)%mod;
IFMT(c[x],n);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin>>n;
len=1<<n;
for(int i=0;i<len;i++)
{
cin>>A[i];
a[bitcnt(i)][i]=A[i];
}
for(int i=0;i<len;i++)
{
cin>>B[i];
b[bitcnt(i)][i]=B[i];
}
Convolution(n);
for(int i=0;i<len;i++) cout<<c[bitcnt(i)][i]<<' ';
}
整理的板子:
namespace FMT
{
#define bitcnt(x) __builtin_popcount(x)
const int mod=998244353ll;
const int Base=21;
inline int mo(int x)
{
if(x>=mod) x-=mod;
if(x<0) x+=mod;
return x;
}
inline int mul(int x,int y)
{
return 1ll*x*y%mod;
}
void FMT(vector<int>& f,int n=Base)
{
int len=1<<n;
for(register int i=0;i<n;i++)
for(register int j=0;j<(1<<n);j++)
if((j>>i)&1) f[j]=mo(f[j]+f[j^(1<<i)]);
}
void IFMT(vector<int>& f,int n=Base)
{
int len=1<<n;
for(register int i=0;i<n;i++)
for(register int j=0;j<(1<<n);j++)
if((j>>i)&1) f[j]=mo(f[j]-f[j^(1<<i)]);
}
vector<int> subset_convolution(vector<int>& A,vector<int> & B,int n=Base)
{
int len=1<<n;
vector<int>H(len);
vector<vector<int> >sH(n+1,vector<int>(len,0)),sA=sH,sB=sH;
for(register int s=0;s<len;++s)
{
sA[bitcnt(s)][s]=A[s];
sB[bitcnt(s)][s]=B[s];
}
for(register int i=0;i<=n;++i)
{
FMT(sA[i],n);
FMT(sB[i],n);
for(register int s=0;s<len;++s)
for(register int j=0;j<=i;++j)
sH[i][s]=mo(sH[i][s]+mul(sA[j][s],sB[i-j][s]));
IFMT(sH[i],n);
}
for(register int s=0;s<len;++s)
H[s]=sH[bitcnt(s)][s];
return H;
}
}
参考资料:
浙公网安备 33010602011771号