『笔记』莫比乌斯反演

写在前面

该来的总会来,终究逃不过圈套。。

爱数学!但是对这种个人认为极其复杂的数学有种莫名的恐惧。。

前置芝士

反演

什么是反演?

参照学长 LB 的 「笔记」反演

积性函数

定义域为正整数的算术函数 \(f(x)\) 满足

\[\left\{ \begin{array}{l} f(n)=1 & n=1 \\ f(pq)=f(p) \cdot f(q) & p \perp q \end{array} \right. \]

一些常见的积性函数

  • 欧拉函数 \(\varphi(n) = \sum \limits_{i=1}^n [\gcd(i,n)=1]\)

  • 莫比乌斯函数 \(\mu(n)\)

  • 最大公约数 \(\gcd(n,k)\)

完全积性函数

定义域为正整数的算术函数 \(f(x)\) 满足

\[\left\{ \begin{array}{l} f(n)=1 & n=1 \\ f(ad)=f(a) \cdot f(b) & a,b \text{任意} \end{array} \right. \]

常见完全积性函数

  • 恒等函数 \(I(n)=1\)

  • 元函数 \(\epsilon(n)=[n=1]\)

  • 单位函数 \(id(n)=n\)

狄利克雷卷积(Dirichiet)

对于两算术函数 \(f,g\) ,定义 \((f * g)(n )=\sum_{d \mid n} f(d) g\left(\frac{n}{d}\right)\) ,其中 \(n\) 常常省略不写。

运算律

交换律

\[f*g=g*f \]

结合律

\[(f*g)*h=f*(g*h) \]

分配律

\[f*(g+h)=f*g+f*h \]

应用

理论 1

\[\mu * I =\epsilon \]

其中

\[\begin{aligned} &I(n)=1,\\ &\epsilon(n)= \left\{ \begin{array}{l} 1 & n=1 \\ 0 & n\neq 1 \end{array} \right. \end{aligned} \]

则有

\[\begin{aligned} \epsilon(n) &= \sum \limits_{d \mid n} \mu(d) \cdot I(\cfrac{n}{d})\\ &=\sum \limits_{d \mid n} \mu(d) \end{aligned} \]

理论 2

\[\varphi * I =id \]

其中 \(id(n)=n\)

则有

\[\begin{aligned} id(n) &= \sum \limits_{d \mid n} \varphi(d) \cdot I(\cfrac{n}{d}) \\ &= \sum \limits_{d \mid n} \varphi(d) \\ &= n \end{aligned} \]

任何数的 \(\varphi\) 之和都是它本身

理论 3

\[\mu * id =\varphi \]

证明:

\[\begin{aligned} \mu * id &= \mu * I * \varphi\\ &= \epsilon * \varphi \end{aligned} \]

其中

\[\begin{aligned} (\epsilon* \varphi) (n) &= \sum \limits_{d \mid n} \epsilon(d) \cdot \varphi(\cfrac{n}{d})\\ &= \varphi(n) \end{aligned} \]

所以 \(\mu * id = \varphi\)

注意:\(\epsilon\) 可看作数论函数中的 \(1\)

莫比乌斯函数

这里详细说明一下上文提到的 \(\mu\) ,即莫比乌斯函数

定义

莫比乌斯函数定义为

\[\mu(n)= \left\{ \begin{array}{ll} 1 & n=1 \\ 0 & n \text { 含有平方因子 } \\ (-1)^{k} & k \text { 为 } n \text { 的本质不同质因子个数 } \end{array} \right. \]

性质

莫比乌斯函数是积性函数,具有一下性质

\[\sum_{d \mid n} \mu(d)=[n=1] \]

证明

\(n=\prod \limits_{i=1}^{k} p_{i}^{c_{i}}, n^{\prime}=\prod \limits_{i=1}^{k} p_{i}\)

莫比乌斯函数定义

\[\sum \limits_{d \mid n} \mu(d)=\sum \limits_{d \mid n^{\prime}} \mu(d) \]

\(n^\prime\) 的某因子 \(d\) ,有 \(\mu(d)=(-1)^\prime\) ,则它由 \(i\) 个本质不同的质因子组成。

由于质因子总数为 \(k\),满足上式的因子数为 \(C_k^i\)

对于原式,可转化为枚举 \(\mu(d)\) 的值

\[\sum \limits_{d \mid n^{\prime}} \mu(d)=\sum \limits_{i=0}^{k} C_{k}^{i} \times(-1)^{i}=\sum_{i=0}^{k} C_{k}^{i} \times(-1)^{i} \times 1^{k-i} \]

根据二项式定理,上式可化为

\[[1+(-1)]^k \]

显然当 \(k=0\) ,即 \(n=0\) 时为 \(1\) ,否则为 \(0\)

线性筛求莫比乌斯函数

\(\mu\) 为积性函数,因此可以线性筛求莫比乌斯函数。

/*
线性筛求莫比乌斯函数

By Luckyblock

有删改。
*/
int pnum, mu[_], p[_];
bool vis[_];

void Euler(int lim_)
{
    vis[1] = true;
    mu[1] = 1ll;
    for (int i = 2; i <= lim_; ++i)
    {
        if (!vis[i])
        {
            p[++pnum] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= pnum && i * p[j] <= lim_; ++j)
        {
            vis[i * p[j]] = true;
            if (!(i % p[j]))
            { //勿忘平方因子
                mu[i * p[j]] = 0;
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
}

莫比乌斯反演

定义

\(f(n),g(n)\) 两个数论函数,

\[g(n)=\sum \limits_{d\mid n} f(d) \]

则有

\[\begin{aligned} f(n) &= (\mu * f)(n) \\ &= \sum \limits_{d \mid n}\mu(d) f(\cfrac{n}{d}) \end{aligned} \]

证明

利用狄利克雷卷积卷积,原问题可化为

已知 \(f = g * I\) ,证明 \(g= f * \mu\)

显然有

\[\begin{aligned} f * \mu &= g * I * \mu \\ &\Longrightarrow f * \mu \\ &= g * e \\ &= g \end{aligned} \]

证毕。

应用

这里有一个套路,一般涉及到莫比乌斯反演的题目都可以按照这个思路来。

zhx 亲授/kel

\(\sum \limits_{i=1}^n \sum \limits_{j=1}^m \gcd(i,j)\) 的值。

“反正这题数据范围暴力肯定过不了,其他无所谓。——zhx

显然,直接暴力枚举 \(O(nmlog)\)肯定过不了。。

能否在 \(O(n)\) 的时间内求解呢?

这里可以用一个非常奇妙的思想——求和变形

  1. 增加枚举变量

    \(id = \varphi * I\)

    \[\begin{align} & \sum \limits_{i=1}^n \sum \limits_{j=1}^m \gcd(i,j) \\ =& \sum \limits_{i=1}^n \sum \limits_{j=1}^m id[ \gcd(i,j)] \\ =& \sum \limits_{i=1}^n \sum \limits_{j=1}^m \sum \limits_{d \mid \gcd(i,j)} \varphi(d) * I \\ =& \sum \limits_{i=1}^n \sum \limits_{j=1}^m \sum \limits_{d \mid \gcd(i,j)} \varphi(d) \end{align} \]

  2. 交换枚举顺序

    \[\begin{align} &\sum \limits_{i=1}^n \sum \limits_{j=1}^m \sum \limits_{d \mid \gcd(i,j)} \varphi(d) \tag{4}\\ &=\sum \limits_{d=1}^n \sum \limits_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum \limits_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(d) \tag{5} \end{align} \]

  3. 删除无用变量

    \[\begin{align} & \sum \limits_{d=1}^n \sum \limits_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum \limits_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(d) \tag{5} \\ =& \sum \limits_{d=1}^n \left\lfloor\cfrac{n}{d}\right\rfloor \left\lfloor\cfrac{m}{d}\right\rfloor \varphi(d) \tag{6} \end{align} \]

然后就可以直接枚举 \(d=1 \sim n\) 求解答案。

时间复杂度 \(O(n)\)

P2398 GCD SUM

/*

Name: P2398 GCD SUM

Solution: 
   

By Frather_

*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define int long long
#define InF 0x3f3f3f3f
#define kMax 10e5
#define kMin -10e5
#define kMod 998244353
using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
int n;

const int _ = 1000010;

int prime[_], v[_], phi[_], cnt;

int res[_];

int ans;
/*=============================================自定义函数*/
void eular(int n) //线性筛筛欧拉函数
{
    memset(v, 0, sizeof(v));
    phi[1] = 1;

    for (int i = 2; i <= n; i++)
    {
        if (!v[i])
        {
            prime[++cnt] = v[i] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt; j++)
        {
            if (prime[j] > v[i] || i * prime[j] > n)
                break;
            v[i * prime[j]] = prime[j];
            phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
        }
        // sumPhi[i] = sumPhi[i - 1] + phi[i];
    }
}
/*=================================================主函数*/
signed main()
{
    n = read();
    eular(n);
    for (int i = 1; i <= n; i++)
        ans += (n / i) * (n / i) * phi[i];
    printf("%lld\n", ans);
    return 0;
}

最后

鸣谢 :

如果存在错误,感谢指出。

posted @ 2021-05-07 21:18  Frather  阅读(102)  评论(0编辑  收藏  举报