多项式算法再探:FMT 和 FWT
我们知道,FFT 和 NTT 可以用来解决下面这种问题:
不过,这并不是卷积的全部形态,比如下面这种:
其中 \(*\) 代表一种位运算。
面对这种位运算类型的卷积,我们也有别样的方法,那就是 FMT 和 FWT。
或卷积
顾名思义,其形态如下:
考虑如下优良性质:
若 \(j|i=i,k|i=i\),则 \((j|k)|i=i\)。
根据这个优良性质,便有了下面两种求解或卷积的方法:
FMT 法
不妨顺着 FFT 的思路,找到 \(FMT(a)_i\times FMT(b)_i=FMT(c)_i\)。
我们构造 \(FMT(a)=\sum\limits_{j|i=i}a_j\),那么就有:
这个时候,FMT 说:“这是高维前缀和。”
于是,问题解决了。直接使用高维前缀和把 \(a\) 和 \(b\) 转成 \(FMT(a)\) 和 \(FMT(b)\),合并后再用高维差分把 \(FMT(c)\) 转为 \(c\)。时间复杂度 \(O(n\log n)\)。
void fmtor(int *a,int fl){
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j]=(a[j]+fl*a[j^(1<<i)])%p;
}
FWT 法
你不要认为 FWT 换了一种构造方式,其实他和 FMT 的构造方式相同。也就是 \(FWT(a)=FMT(a)\)。
不过 FWT 说:“这玩意可以分治。”
于是考虑使用类似 FFT 的方式分治处理。当然,说是分治,但有了 FFT 的经验,写出来的代码是迭代形式的。
设 \(merge(a,b)\) 表示将 \(b\) 序列接到 \(a\) 序列后面,\(a_0\) 表示 \(a\) 序列的左半部分,\(a_1\) 表示 \(a\) 序列的右半部分,那么就有:
原因非常简单。假如当前枚举的 \(i\) 为 \(0\) 的话,那么你就只能选择 \(0\)(不然或出来的结果会是 \(1\)),而为 \(1\) 的话,这一位就无所谓了。
逆运算就是给后半部分减去前半部分的贡献,即:
时间复杂度相同。
void fwtor(int *a,int fl){
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++)
a[l+len+k]=(a[l+len+k]+fl*a[l+k])%p;
}
与卷积
依旧顾名思义,其形态如下:
同样发现如下优良性质:
若 \(j\&i=i,k\&i=i\),则 \((j\&k)\&i=i\)。
根据这个优良性质,便仍然有下面两种求解与卷积的方法:
FMT 法
我们构造 \(FMT(a)=\sum\limits_{j\&i=i}a_j\),那么就有:
这个时候,FMT 说:“这是高维后缀和。”
于是,问题再一次解决了。直接使用高维后缀和把 \(a\) 和 \(b\) 转成 \(FMT(a)\) 和 \(FMT(b)\),合并后再用高维差分把 \(FMT(c)\) 转为 \(c\)。时间复杂度 \(O(n\log n)\)。
void fmtand(int *a,int fl){
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j^(1<<i)]=(a[j^(1<<i)]+fl*a[j])%p;
}
FWT 法
FWT 又一次说:“这玩意仍然可以分治。”
观察后发现有:
逆运算就是给前半部分减去后半部分的贡献,即:
时间复杂度相同。
void fwtand(int *a,int fl){
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++)
a[l+k]=(a[l+k]+fl*a[l+len+k])%p;
}
异或卷积
依旧顾名思义,其形态如下:
同样发现如下优良性质:
若 \(j\oplus i=i,k\oplus i=i\),则 \((j\oplus k)\oplus i=i\),且 \(j=k=0\)。
这个性质优良到了我们必须改变思路的程度了,也是太过于优良了。
高维前缀和和高维后缀和都用过了,此时 FMT 并不容易解决这个问题(其实是能解决的,只不过有点牵强),而 \(FWT(a)\) 感觉也相当不好想。
既然异或没有优良性质,我们就要构造一种运算,使它自身有优良性质,且能和异或搭上关系。
我们引入一种新的运算 \(\bullet\),满足 \(x\bullet y=count(x\& y)\bmod 2\),其中 \(count(x)\) 表示 \(x\) 在二进制下 \(1\) 的个数。
那么就有如下优良性质:
\[(x\bullet y)\oplus(x\bullet z)=x\bullet(y\oplus z) \]证明:设:
\[S=\{i|2^i\& x>0,2^i\& y>0\} \]\[T=\{i|2^i\& x>0,2^i\& z>0\} \]\[G=\{i|2^i\& x>0,2^i\& (y\oplus z)>0\} \]容易发现:
\[(x\bullet y)\oplus(x\bullet z)=(|S|+|T|)\bmod 2 \]\[x\bullet(y\oplus z)=|G|\bmod 2 \]考虑到异或后消掉部分一定在 \(y,z\) 中都存在。换言之,就是:
\[|S|+|T|-2|S\cap T|=|G| \]\[(|S|+|T|)\bmod 2=(|G|)\bmod 2 \]\[(x\bullet y)\oplus(x\bullet z)=x\bullet(y\oplus z) \]\(Q.E.D.\)
设 \(FWT(a)_i=\sum\limits_{j\bullet i=0}a_j-\sum\limits_{j\bullet i=1}a_j\),则有:
考虑分治方式。当前位 \(i\) 为 \(0\) 时,\(i\bullet j\) 必为 \(0\),所以 \(0,1\) 贡献都为正。同理,\(i=1\) 时,\(0\) 贡献为正,\(1\) 贡献为负。所以有:
时间复杂度不变。
void fwtxor(int *a,int fl){
fl=(fl==1)?1:(p+1)/2;
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++){
int a0=a[l+k],a1=a[l+len+k];
a[l+k]=(a0+a1)*fl%p;
a[l+len+k]=(a0-a1)*fl%p;
}
}
其实也可以把它写成 FMT 的形式:
void fmtxor(int *a,int fl){
fl=(fl==1)?1:(p+1)/2;
for(int i=0;i<n;i++)
for(int j=0;j<(1<<n);j++)
if(~j&(1<<i)){
int a0=a[j],a1=a[j|(1<<i)];
a[j|(1<<i)]=(a0-a1)*fl%p;
a[j]=(a0+a1)*fl%p;
}
}
最后放上模板题代码:
//FMT 342ms
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=(1<<17),p=998244353;
int n,f[N],g[N],c[N],a[N],b[N];
void fmtor(int *a,int fl){
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j]=(a[j]+fl*a[j^(1<<i)])%p;
}void fmtand(int *a,int fl){
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j^(1<<i)]=(a[j^(1<<i)]+fl*a[j])%p;
}void fmtxor(int *a,int fl){
fl=(fl==1)?1:(p+1)/2;
for(int i=0;i<n;i++)
for(int j=0;j<(1<<n);j++)
if(~j&(1<<i)){
int a0=a[j],a1=a[j|(1<<i)];
a[j|(1<<i)]=(a0-a1)*fl%p;
a[j]=(a0+a1)*fl%p;
}
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0),cin>>n;
for(int i=0;i<(1<<n);i++)
cin>>a[i],f[i]=a[i];
for(int i=0;i<(1<<n);i++)
cin>>b[i],g[i]=b[i];
fmtor(f,1),fmtor(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fmtor(c,-1);
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
for(int i=0;i<(1<<n);i++) f[i]=a[i];
for(int i=0;i<(1<<n);i++) g[i]=b[i];
fmtand(f,1),fmtand(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fmtand(c,-1),cout<<"\n";
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
for(int i=0;i<(1<<n);i++) f[i]=a[i];
for(int i=0;i<(1<<n);i++) g[i]=b[i];
fmtxor(f,1),fmtxor(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fmtxor(c,2),cout<<"\n";
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
return 0;
}
//FWT 298ms
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=(1<<17),p=998244353;
int n,f[N],g[N],c[N],a[N],b[N];
void fwtor(int *a,int fl){
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++)
a[l+len+k]=(a[l+len+k]+fl*a[l+k])%p;
}void fwtand(int *a,int fl){
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++)
a[l+k]=(a[l+k]+fl*a[l+len+k])%p;
}void fwtxor(int *a,int fl){
fl=(fl==1)?1:(p+1)/2;
for(int len=1;len<(1<<n);len<<=1)
for(int l=0;l<(1<<n);l+=(len<<1))
for(int k=0;k<len;k++){
int a0=a[l+k],a1=a[l+len+k];
a[l+k]=(a0+a1)*fl%p;
a[l+len+k]=(a0-a1)*fl%p;
}
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0),cin>>n;
for(int i=0;i<(1<<n);i++)
cin>>a[i],f[i]=a[i];
for(int i=0;i<(1<<n);i++)
cin>>b[i],g[i]=b[i];
fwtor(f,1),fwtor(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fwtor(c,-1);
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
for(int i=0;i<(1<<n);i++) f[i]=a[i];
for(int i=0;i<(1<<n);i++) g[i]=b[i];
fwtand(f,1),fwtand(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fwtand(c,-1),cout<<"\n";
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
for(int i=0;i<(1<<n);i++) f[i]=a[i];
for(int i=0;i<(1<<n);i++) g[i]=b[i];
fwtxor(f,1),fwtxor(g,1);
for(int i=0;i<(1<<n);i++)
c[i]=f[i]*g[i]%p;
fwtxor(c,2),cout<<"\n";
for(int i=0;i<(1<<n);i++)
cout<<(c[i]+p)%p<<" ";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)