快速莫比乌斯/沃尔什变换 (FMT/FWT)

一、快速莫比乌斯变换

快速莫比乌斯变换简称 FMT ,用于快速计算位运算卷积。

我们定义 A 的莫比乌斯变换为 FMT(A)AiA 的第 i 项。

1、或卷积

我们要求:Cx=ij=xAiBj

若有 FMT(A)i×FMT(B)i=FMT(C)i ,我们就能通过逆变换快速求出 C 出来。

定义:FMT(A)n=inAi ,其中 i,n 都是二进制表示的集合。

FMT(A)x×FMT(B)x=ixAijxBj=ixjxAiBj=kxij=kAiBj=kxCk=FMT(C)x

所以利用子集和可以加速求出或卷积。

那如何快速求出子集和捏?不就是高维前缀和了捏!

高维前缀和:Si=ij=iAj

code:

void FMT(int *f,int n,int op)//op=1为正变换,op=-1为逆变换
{
    for(int i=0;i<n;++i)
        for(int j=0;j<(1<<n);++j)
            if(j&(1<<i))f[j]+=f[j^(1<<i)]*op;
}

2、与卷积

我们要求:Cx=ij=xAiBj

若有 FMT(A)i×FMT(B)i=FMT(C)i ,我们就能通过逆变换快速求出 C 出来。

定义:FMT(A)n=niAi ,其中 i,n 都是二进制表示的集合。

FMT(A)x×FMT(B)x=xiAixjBj=xixjAiBj=xkij=kAiBj=xkCk=FMT(C)x

这不就是高维后缀和了捏!

高维后缀和:Si=ij=iAj

code:

void FMT(int *f,int n,int op)//op=1为正变换,op=-1为逆变换
{
    for(int i=0;i<n;++i)
        for(int j=0;j<(1<<n);++j)
            if(j&(1<<i))f[j^(1<<i)]+=f[j]*op;
}

二、快速沃尔什变换

快速沃尔什变换简称 FWT ,同样用于快速计算位运算卷积。

我们定义 A 的沃尔什变换为 FWT(A)AiA 的第 i 项。

1、异或卷积

定义:FWT(A)x=k=02n1(1)|xk|Ak

FWT(C)x=k=02n1(1)|xk|Ck

                  =k=02n1(1)|xk|i=02n1j=02n1[ij=k]AiBj

                  =i=02n1j=02n1(1)|(ij)x|AiBj

                  =i=02n1j=02n1(1)|xi|Ai·(1)|xj|Bj

                  =(i=02n1(1)|xi|Ai)(j=02n1(1)|xj|Bj)

                  =FWT(A)x×FWT(B)x

和高维前缀和相似,我们对每一位依次考虑。对于第 i 位和一个不包含 i 的集合 S ,设 x=aS,y=aS+2i ,则有新的 aS=x+y,aS+2i=xy

code:

void FWT(int *f,int n,int op)
{
    for(int i=1;i<(1<<n);i<<=1)
        for(int j=0;j<(1<<n);j+=(i<<1))
            for(int k=j;k<j+i;++k)
            {
                int x=f[k],y=f[k+i];
                if(op==1)f[k]=x+y,f[k+i]=x-y;
                else f[k]=(x+y)/2,f[k+i]=(x-y)/2;
            }
}

是不是很简单

当然,FMT 也可以计算或卷积和与卷积,但懒得打了(咕咕咕

2、子集卷积

upt:2022.7.7
其实半年前就学了这东西,但忘了补到博客上来了(

期望构造一个占位函数 w(x) 使得 i&j=0,i|j=k 当且仅当 ij=k,w(i)+w(j)=w(k),容易构造 w(x)=|x|

fi,j={Ajw(j)=i0w(j)i,gi,j={Bjw(j)=i0w(j)i

计算 hk=i=0kfigki,最后答案为 ck=hw(k),k。时间复杂度为 O(2nn2)

代码:

#include<bits/stdc++.h>
#define pc(x) putchar(x)
#define ll long long
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void write(int x)
{
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+48);
}
const ll mod=1e9+9;
int n;
int a[21][3000005],b[21][3000005],c[21][3000005];
int cnt(int x){return __builtin_popcount(x);}
void FMT(int *f,int n,int op)
{
    for(int i=0;i<n;++i)
        for(int j=0;j<(1<<n);++j)
            if(j&(1<<i))f[j]+=f[j^(1<<i)]*op,f[j]=((ll)f[j]+mod)%mod;
}
int main()
{
    n=read();
    for(int i=0;i<(1<<n);++i)
        a[cnt(i)][i]=read();
    for(int i=0;i<(1<<n);++i)
        b[cnt(i)][i]=read();
    for(int i=0;i<=n;++i)FMT(a[i],n,1),FMT(b[i],n,1);
    for(int i=0;i<=n;++i)
        for(int j=0;j<=i;++j)
            for(int k=0;k<(1<<n);++k)
                c[i][k]=(ll)(c[i][k]+(ll)a[j][k]*b[i-j][k]%mod)%mod;
    for(int i=0;i<=n;++i)FMT(c[i],n,-1);
    for(int i=0;i<(1<<n);++i)write(c[cnt(i)][i]),pc(' ');
    return 0;
}

代码是我自己打的,算法过程是搬APIO2022蔡欣然巨佬讲课的PDF。

3、子集卷积 exp

咕咕咕

4、多项式复合集合幂级数

posted @   violetctl39  阅读(832)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示