c莫比乌斯函数_数论小白入门-- 莫比乌斯反演

0cfd3bfcc82870d3c42ee6aac84d9830.png

写在最前

生活所迫。数论小白开始入门数论了。

会陆陆续续发一些自己的笔记和总结。

原文链接

莫比乌斯反演学习笔记​xiejiadong.com
da7f3d266bc4a199ae613d63136aeb9d.png

数论函数

定义域为正整数的函数称为数论函数。

积性函数

如果

,这样的数论函数称为积性函数。

常见的数论函数:

  • 欧拉函数(如果
    ,则有
  • 莫比乌斯函数
  • 除数函数 ,用
    表示。其值等于所有
    的因子的
    次方之和。

完全积性函数

如果

,这样的数论函数称为完全积性函数。

常见的完全积性函数有:

Dirichlet 卷积

两个数论函数

的 Dirichlet 卷积为:

其中 Dirichlet 卷积的单位元定义为

,且

【结论】如果

均为积性函数,则
也为积性函数。

莫比乌斯函数

如果

含有平方因子,那么
;否则
,其中
的本质不同的质因子个数。

【性质】

.
【证明】我们令

显然,枚举
的因子和枚举
因子的差异就在于少枚举了含有平方因子的因子。

则有

此时的
,我们就可以考虑在
中任意选取组成一个因子了,于是就有

也就是只有当
的时候
,其他时候

而显然当
的时候,
,于是就证明了。

莫比乌斯反演

是两个数论函数,如果有
,那么有
【证明】因为我们有
,而
其实就是

于是
也就是

不过一般情况下构造一个

形式的式子是比较难的,一般情况下我们会直接化成
的形式,然后通过
来计算。

一种比较常见的问题是这样的:求

.

我们考虑枚举

的结果,假设
,于是就有

考虑将

部分反演,则有

我们令

则有

【例1】HAOI2011 Problemb

题意

分析

因为

,其中

我们令

并且假设

于是就有

因为有多组询问,这样的时间复杂度仍然是不允许的。

我们可以考虑数论分块,所谓数论分块,无非是

这部分的取值是
级别的,所以我们可以考虑把相同的一起算。
#include<bits/stdc++.h>
#define LL long long
using namespace std;

const LL p_max = 100010;
LL mu[p_max];
void get_mu() {
    mu[1] = 1;
    static bool vis[p_max];
    static LL prime[p_max], p_sz, d;
    for (int i=2;i<p_max;i++)
    {
        if (!vis[i]) {
            prime[p_sz++] = i;
            mu[i] = -1;
        }
        for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
            vis[d] = 1;
            if (i % prime[j] == 0) {
                mu[d] = 0;
                break;
            }
            else mu[d] = -mu[i];
        }
    }
}

LL T,a,b,n,m,k,f[p_max];

LL calc(LL n,LL m)
{
    if(n>m) swap(n,m);
    LL ans=0;
    for (LL i=1;i<=n/k;)
    {
        LL p=n/i/k,q=m/i/k;
        p=min(n/k,n/p/k),q=min(n/k,m/q/k);
        p=min(p,q);
        ans+=(f[p]-f[i-1])*(n/i/k)*(m/i/k);
        //cout<<"p,q="<<p<<" "<<q<<endl;
        //cout<<i<<" "<<" "<<(f[p]-f[i-1])<<" "<<(f[p]-f[i-1])*(n/i/k)*(m/i/k)*(p-i+1)<<endl;
        i=p+1;
    }
    return ans;
}

void init()
{
    f[0]=0;
    for (int i=1;i<p_max;i++)
        f[i]=f[i-1]+mu[i];
}

int main()
{
    get_mu();init();
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld%lld",&a,&n,&b,&m,&k);
        printf("%lldn",calc(n,m)-calc(a-1,m)-calc(n,b-1)+calc(a-1,b-1));
    }
    return 0;
}

【例2】SPOJ5971 LCMSUM

题意

分析

我们并不太会直接求 lcm ,于是考虑转换成 gcd 来做。

对于 gcd 而言,显然有

,于是:

.

考虑枚举

的值,如果
,显然有
,那么显然这样的
个。

于是有

.

,这部分显然可以通过线性筛预处理出来,于是每次询问就
了。
#include<bits/stdc++.h>
#define LL long long
using namespace std;

const LL p_max = 1000100;
LL phi[p_max];
void get_phi() {
    phi[1] = 1;
    static bool vis[p_max];
    static LL prime[p_max], p_sz, d;
    for (int i=2;i<p_max;i++){
        if (!vis[i]) {
            prime[p_sz++] = i;
            phi[i] = i - 1;
        }
        for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
            vis[d] = 1;
            if (i % prime[j] == 0) {
                phi[d] = phi[i] * prime[j];
                break;
            }
            else phi[d] = phi[i] * (prime[j] - 1);
        }
    }
}

LL T,n,f[p_max],g[p_max];

int main()
{
    get_phi();
    for (int i=1;i<p_max;i++)
        f[i]=phi[i]*i;
    memset(g,0,sizeof(g));
    for (int i=2;i<p_max;i++)
        for (int j=i;j<p_max;j+=i)
            g[j]+=f[i];
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        printf("%lldn",g[n]*n/2LL+n);
    }
    return 0;
} 

【例3】hdu4944 FSF’s game

题意

分析

因为

所以有

显然

,于是

上一个例题解决了

这个问题,我们不妨设式子的后半部分为
。这部分其实就是在此基础上再前缀和,有了上一题,可以很容易解决。

于是现在就是求

,显然
取值是
级别的,可以数论分块,于是我们可以有
的做法。

Tle 了两发。过不去。只能考虑优化。

我们考虑枚举

,然后计算
的贡献。

假设当前

,那么当前的
的贡献区间是
,且贡献为

可以考虑在前后打上标记,然后前缀和预处理计算。

此时复杂度变成了

大概近似于
就可以过了。
#include<bits/stdc++.h>
#define LL long long
using namespace std;

const LL p_max = 500100;
LL phi[p_max];
void get_phi() {
    phi[1] = 1;
    static bool vis[p_max];
    static LL prime[p_max], p_sz, d;
    for (int i=2;i<p_max;i++){
        if (!vis[i]) {
            prime[p_sz++] = i;
            phi[i] = i - 1;
        }
        for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) {
            vis[d] = 1;
            if (i % prime[j] == 0) {
                phi[d] = phi[i] * prime[j];
                break;
            }
            else phi[d] = phi[i] * (prime[j] - 1);
        }
    }
}

LL T,n,f[p_max],g[p_max],sum[p_max];

int main()
{
    get_phi();
    for (int i=1;i<p_max;i++)
        f[i]=phi[i]*i;
    memset(g,0,sizeof(g));
    for (int i=2;i<p_max;i++)
        for (int j=i;j<p_max;j+=i)
            g[j]+=f[i];
    for (int i=1;i<p_max;i++)
        f[i]=g[i]*i/2LL+i;
    f[0]=0;
    for (int i=1;i<p_max;i++)
        f[i]+=f[i-1];
    for (LL d=1;d<p_max;d++)
    {
        for (LL i=1;i*d<p_max;i++)
        {
            sum[d*i]+=d*d*f[i];
            if (d*(i+1)<p_max) sum[d*(i+1)]-=d*d*f[i];
        }
    }
    sum[0]=0;
    for (int i=1;i<p_max;i++)
        sum[i]+=sum[i-1];
    scanf("%lld",&T);int cass=0;
    while(T--)
    {
        scanf("%lld",&n);
        printf("Case #%d: %un",++cass,(unsigned)sum[n]);
    }
    return 0;
}

【例4】湖北省队互测 一个人的数论

题意

求所有

且与
互质的数的
次幂的和。

分析

题目要求的就是

用莫比乌斯反演

变换求和的顺序

显然

这部分是积性函数。于是考虑变换后一部分。

我们令

,显然这是一个关于
次多项式,于是可以用拉格朗日插值把系数都求出来,变成

于是原式就变成了

变换求和顺序变成

显然

是一个卷积的形式。

因为

都是积性函数,那么
也是一个积性函数。

我们令

,且有
。那么有

考虑对于任意的

,只有当
或者
的时候
于是只需要求这两项,即
#include<bits/stdc++.h>
#define LL long long
using namespace std;


LL bin(LL x,LL n,LL MOD)
{
    LL ret=MOD!=1;
    for (x%=MOD;n;n>>=1,x=x*x%MOD)
        if (n&1) ret=ret*x%MOD;
    return ret;
}

const LL MOD=1000000007;
const int MAXN = 1005;
typedef vector<LL> Poly;
Poly operator * (const Poly &a, const Poly &b) {
    Poly c(a.size() + b.size() - 1, 0);
    int an = a.size(), bn = b.size();
    for (int i=0; i<an; ++i)
        for (int j=0; j<bn; ++j)
            c[i+j] = (c[i+j] + a[i]*b[j])%MOD; 
    while (!c.empty() && c.back() == 0) c.pop_back();
    return c;
}
Poly operator + (const Poly &a, const Poly &b)
{
    int an = a.size(), bn = b.size(), n = max(an, bn); Poly c; c.resize(n);
    for (int i=0; i<n; ++i)
    {
        c[i] = 0;
        if (i < an) c[i] += a[i];
        if (i < bn) c[i] += b[i];
        while (c[i] >= MOD) c[i] -= MOD;
    }
    while (!c.empty() && c.back() == 0) c.pop_back();
    return c;
}
void operator *= (Poly &a, LL b){
    int n = a.size();
    for (int i=0; i<n; ++i)
        a[i] = a[i] * b % MOD;
}
Poly g[MAXN];
LL c[MAXN];
Poly go(LL x[], LL y[], int n) {
    for (int i=0; i<n; ++i){
        LL B = 1; g[i].push_back(1);
        for (int j=0; j<n; ++j) {
            if (i == j) continue;
            B = B * (x[i] - x[j] + MOD) % MOD;
            Poly p;p.push_back(MOD-x[j]);p.push_back(1);
            g[i] = g[i] * p;
        }
        g[i] *= (MOD + y[i]) * bin(B, MOD-2 ,MOD)%MOD;
    }
    Poly res;res.clear();
    for (int i=0; i<n; ++i) { res = res + g[i]; }
    return res;
}
LL n,d,w,x[MAXN],y[MAXN],p[MAXN],a[MAXN];

int main()
{
    scanf("%lld%lld",&d,&w);
    n=1;
    for (int i=1;i<=w;i++)
        scanf("%lld%lld",&p[i],&a[i]),n*=bin(p[i],a[i],MOD),n%=MOD;
    x[0]=0;y[0]=bin(0,d,MOD);
    for (int i=1;i<=d+1;i++)
        x[i]=i,y[i]=(y[i-1]+bin(i,d,MOD))%MOD;
    Poly res=go(x,y,d+2);
    LL ans=0;
    for (int i=0;i<=d+1;i++)
        x[i]=res[i];
    for (int i=0;i<=d+1;i++)
    {
        LL sum=1;
        for (int j=1;j<=w;j++)
        {
            LL x=p[j],y=a[j];
            sum=(sum*((bin(bin(x,y,MOD),i,MOD)%MOD-bin(x,d,MOD)*bin(bin(x,y-1,MOD),i,MOD)%MOD+MOD)%MOD))%MOD;
            //cout<<((mu[1]*bin(x,i,MOD)%MOD+mu[x]*bin(x,d,MOD)%MOD)%MOD)<<endl;
        }
        ans+=x[i]*sum;
        ans%=MOD;
        //printf("x[i]=%lld sum=%lld ans=%lldn",x[i],sum,ans);
    }
    printf("%lldn",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值