【学习笔记】莫比乌斯反演

前置知识:

积性函数:

对于互质整数a ,b,如果有f(a×b)=f(a)×f(b) ,那么称f(x)为积性函数,例如 φ,μ,σ,d

而完全积性函数是对于任意的整数a , b,有f(a×b)=f(a)×f(b)的数论函数如 ϵ,I,id

Mobius 函数( μ ):

μ(n)={1,n=1(1)m,n=i=1n pi0,otherwise

可以看出,μ(n) 恰在 n 无平方因子时取值非零。

d|nμ(d)=ϵ(n)ϵ=1μ

可以线性筛

inline void prework(){
        miu[1]=1; 
        for(int i=2;i<N;++i){
            if(!vis[i]) pri[cnt++]=i,miu[i]=-1;
            for(int j=0;j<cnt&&i*pri[j]<N;++j){}
                vis[i*pri[j]]=1; if(i%pri[j]) miu[i*pri[j]]=-miu[i];
                else{miu[i*pri[j]]=0; break;} 
            } miu[i]+=miu[i-1];
        } return ;
    }

Dirichlet 卷积

h=fgh(n)=d|nf(d)·g(nd)h(n)=i·j=nf(i)·g(j)

同时 ϵ 函数是 Dirichlet 卷积的单位元,即 f=fϵ

常见的卷积式子有: μI=ϵφI=idμid=φ

这些卷积的正确性证明都可以通过考察因数的计算次数得到

Mobius 反演

f 是数论函数,定义函数 g 满足

g(n)=d|nf(d)f(n)=d|nμ(d) g(nd)

证明先直接带入并整理,再根据 Iμ=ϵ 即可得到除了 x=n 之外的 f(x)f(n) 中的贡献次数为 0f(n) 的贡献次数为 1

例题

推式子一般使用如下方式

1.枚举 gcd,除掉 d

2.[gcd(i,j)=1]d|i,d|jμ(d)

3.T=dp 然后提出来 T

感觉还是理解反演本质非常重要,甚至很多题目都是使用最本源的莫比乌斯反演公式设两个函数就可以得到非常简洁的,可以求解的式子

约数个数和

通过下式将原式展开并使用 nx 的倍数个数就能得到简洁表达

d(ij)=x|iy|j[gcd(x,y)=1]

最终表达式是积性函数求和,需要搭配线性筛

一个人的数论

fd(n)=p|npdμ(p)i=1npid

设自然数幂前缀和 Sd(n)=i=1nid,那么则有

fd(n)=p|npdμ(p)Sd(np)

将自然数幂表示为多项式,设数列 {a} 为其系数:

fd(n)=i=0d+1ai×(p|npd(np)iμ(p))

然后看看后面的部分就是两个函数 u(x)=xdμ(x)v(x)=xi 的狄利克雷卷积,可以线性筛得到,对于这里而言就是把互质的函数值乘起来

(uv)i(pa)μ 的值为 0 的部分消掉之后剩下了:pai(1pdi)

Code Display
const int mod=1e9+7,N=1010;
inline int ksm(int x,int y){
    if(y<0) return ksm(ksm(x,-y),mod-2);
    int res=1; for(;y;y>>=1,(x*=x)%=mod) if(y&1) res=res*x%mod;
    return res;
}
int ans,n,d,p[N][2],m[N][N],res[N],sum[N];
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
    d=read(); n=read();
    for(int i=1;i<=n;++i) p[i][0]=read(),p[i][1]=read();
    for(int i=0;i<=d+2;++i){
        int res=0; for(int j=1;j<=i+1;++j) res=add(res,ksm(j,d));
        m[i][d+2]=res; m[i][0]=1;
        for(int j=1;j<=d+1;++j) m[i][j]=m[i][j-1]*(i+1)%mod;
    }
    for(int i=0;i<=d+2;++i){
        int t=i;
        for(int j=i;j<=d+2;++j) if(m[j][i]){t=j; break;}
        for(int j=i;j<=d+2;++j) swap(m[i][j],m[t][j]);
        for(int j=i+1;j<=d+2;++j){
            int x=m[j][i]*ksm(m[i][i],mod-2)%mod;
            for(int k=i;k<=2+d;++k) m[j][k]=(m[j][k]-m[i][k]*x%mod+mod)%mod; 
        } 
    }
    for(int i=d+2;i>=0;--i){
        m[i][d+2]=m[i][d+2]*ksm(m[i][i],mod-2)%mod;
        for(int j=i-1;j>=0;--j) m[j][d+2]-=m[i][d+2]*m[j][i]%mod,m[j][d+2]=(m[j][d+2]+mod)%mod;
    }
    for(int i=0;i<=d+1;++i) res[i]=m[i][d+2]; 
    // Solve Equation
    for(int i=0;i<=d+1;++i){
        int tmp=1;
        for(int j=1;j<=n;++j){
            tmp=tmp*ksm(p[j][0],p[j][1]*i)%mod*((1-ksm(p[j][0],d-i)+mod)%mod)%mod; 
        }
        ans=add(ans,res[i]*tmp%mod);
    } cout<<ans<<endl;
    return 0;
}

2021-03-29 数列

原题可以被分成快速求 A,B 数列和求行列式两部分

第一部分考虑将原题中的式子分成 n4 的模数进行考虑

那么不难用 logn 的时间将原题转化成一个 n4 的子问题,复杂度 nlog2n ,不能通过

考虑预处理序列的前 n 位那么复杂度变为 Θ(n+klog2maxn)

第二问考虑设 BF(x)=k|xg(k),g(x)=k|xμ(xk)BF(k)

所以矩阵会转化成 Mati,j=[i|j]g(i),证明考虑归纳,然后手动消下一行的元

这部分复杂度 Θ(nlogn)

posted @   没学完四大礼包不改名  阅读(256)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示