浅述狄利克雷卷积,莫比乌斯函数,莫比乌斯反演

前言

  • TTC讲的太好了全听懂了呢谢谢你喵~/bx
  • upd on 8.15 16:25:添加证明

卷积

  • 相当于莫反的前置芝士了,我们讲狄利克雷卷积
  • 先说个概念:
    • 数论函数:定义域为 \(Z^+\) 的函数
  • 那对于两个数论函数 \(f\)\(g\),我们有

\[h(n) = \sum\limits_{d|n}{f(n) \times g({n \over d})} \]

  • 这个运算叫做卷积运算,简称,符号为 \(\ast\),上面的运算记为 \(h = f \ast g\)
  • 这个运算都叫卷积了,他和正常的积运算一样满足交换律,结合律,分配率
  • 还有一个性质,是证莫反的过程需要的,但是我们要先说三个新概念

\[I = <1,1,1,\dots> \]

\[I_d = <1,2,3,\dots> \]

\[\epsilon = <1,0,0,\dots> \]

  • 其中 \(I\) 称为常数函数,\(I_d\) 称为恒等函数,\(\epsilon\) 是希腊字母,读作 \(epsilon\),为单位函数(欸这个常数函数符号是 \(I\) 还是 \(1\) a,算了写错会有人说的)
  • 那会有一个性质:

\[f = f \ast \epsilon \]

  • 可以徒手算一下
  • 其实卷积也可以做求逆的运算,msjing不会就先不说了
  • 还有一个定义,一并介绍了
积性函数:
  • 定义一个数论函数 \(f(x)\),如果 \(f(x) \times f(y) = f(x \times y)\) \((gcd(x,y) = 1)\),则称 \(f(x)\) 为积性函数
  • 广义的积性函数就是把互质的限制去掉
  • 比如欧拉函数 \(\varphi (x)\) 和要说的莫比乌斯函数 \(\mu (x)\) 都是积性函数

莫比乌斯函数

  • 莫比乌斯函数记作 \(\mu (x)\),有以下定义

\[ ​\mu (n)=\begin{cases} 1 & (n=1)\\ (-1)^k & (n = p_1^1 + p_2^1 + \dots + p_k^1)\\ 0 & (else) \end{cases} ​\]

  • 形象地,当 \(n = 1\) 时,\(\mu (n) = 1\);当 \(n\) 的所有质因数只出现了一次时:若有奇数个质因数,\(\mu (n) = -1\),若有偶数个质因数,\(\mu (n) = 1\);若质因数有相同的,\(\mu (n) = 0\)
  • 我们就知道了莫比乌斯函数了,求的话可以用欧拉筛(线性筛)直接求,实现给一下吧
点击查看代码
void init(int n)
{
	mo[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (!is_no_p[i]) prime[++prime[0]]=i,mo[i]=-1;
		for (int j=1;j<=prime[0] && i*prime[j]<=n;j++)
		{
			is_no_p[i*prime[j]]=1;
			if (i%prime[j] == 0) {mo[i*prime[j]]=0;break;}
			mo[i*prime[j]]=mo[i]*(-1);
		}
	}
}
  • 好像是这个,有锅来找sbmqwm
  • sbmqwm:?
  • msjing:总有一种不好的预感
  • 接下来介绍性质,有一个重要的性质,是推莫反的基础

\[\sum\limits_{d|n}{\mu (d) = [n = 1]} \]

证明
  • 对于 $ \sum\limits_{d|n}{\mu (d)} $,我们把 \(\mu (d)\) 改为 \(\mu (\left\lfloor\dfrac nd\right\rfloor)\),这个显然,因为你怎么枚举约数都一样,原式变成:

\[\sum\limits_{d|n}{\mu (\left\lfloor\dfrac nd\right\rfloor)} \]

  • \(n\) 分解质因数,根据算数基本定理,有:

\[n = p_1^{c_1} + p_2^{c_2} + \dots + p_k^{c_k} \]

  • 我们任取一些 \(p\),其中对应的 \(c\ne1\) 对答案没贡献,不管,我们直接尝试枚举约数,原式变成:

\[\sum\limits_{i=0}^{k}\binom{k}{i}(-1)^i \]

  • 我们直接用二项式定理转化,不会看看这里,原式变为:

\[(1-1)^k \]

  • 即:

\[0^k \]

  • \(k=0\)\(n=1\) 时,原式为 \(1\),其余情况为 \(0\),得证
  • 好像证毕还可以用QED
  • 其中的 \([n = 1]\) 可以理解为 C++ 的 n == 1,成立返回 \(1\),否则返回 \(0\)
  • 我们还可以扩展一下,把 \(n\) 改为 \(\gcd(i,j)\)(你发现任意一个 \(n\) 都可以用两个数的 \(\gcd\) 表示)
  • 那就会有:

\[[\gcd(i,j) = 1] = \sum\limits_{d|\gcd(i,j)}{\mu (d)} \]

  • 这个东西可以写为:

\[\sum\limits_{d}{[d|i] \times [d|j] \times \mu (d)} \]

  • 这样的话就把互质和莫比乌斯函数联系起来了
  • 还有一个性质:

\[I \ast \mu = \epsilon \]

  • 这个也是证莫反的关键

莫比乌斯反演

  • 这个东西挺简单,但是这个东西用起来就是个东西
  • 好!(激情)我们开始介绍莫反
  • 莫比乌斯反演和二反差不多(?),都是两个柿子知道一个可以推另一个
  • 常见柿子为:

\[f(n) = \sum\limits_{d|n}{g(d)} \iff g(n) = \sum\limits_{d|n}{\mu ({n \over d}) \times f(d)} \]

  • 我们发现这个东西长得像卷积,我们化一下,就有第二个形式:

\[f = I \ast g \iff g = \mu \ast f \]

  • 我们依据第二个形式给出证明
  • 证明挺简单
证明
  • 我们直接把左边带入,有

\[g = \mu \ast I \ast g \]

  • 由于结合律,有:

\[g = (\mu \ast I) \ast g \]

  • 我们上面提过一个性质:\(I \ast \mu = \epsilon\),所以有:

\[g = \epsilon \ast g \]

  • 就化为:

\[g = g \]

  • 证毕!
  • 基础搞完了,讲道题

例题P2257 YY的GCD

  • e,默认你会整除分块(数论分块)
  • 我们直接转化题意,相当于求:

\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m} [\gcd(i,j) \in p] \]

  • 其中,\(p\) 代表素数
  • 我们发现这个东西完全不可做,所以我们尝试直接枚举 \(p\),就有:

\[\sum\limits_{p}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m} [\gcd(i,j) = p] \]

  • 还是不好做,我们把 \(p\) 直接除掉,有:

\[\sum\limits_{p}\sum\limits_{i=1}^{\lfloor{n \over p}\rfloor}\sum\limits_{j=1}^{\lfloor{m \over p}\rfloor} [\gcd(i,j) = 1] \]

  • 我们发现后面的这个可以用莫反转化,就有:

\[ \sum\limits_{p}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m} \sum\limits_{d} [d|i] \times [d|j] \times \mu (d) \]

  • 其中,\(d\) 是约数
  • 我们发现这个 \(\sum\limits_{d}\) 有点碍事,可以直接交换枚举顺序,直接把这个东西提前,由于莫比乌斯函数仅与 \(d\) 有关,故也可以提前,有:

\[\sum\limits_{p}\sum\limits_{d}\mu (d)\sum\limits_{i=1}^{\lfloor{n \over p}\rfloor}\sum\limits_{j=1}^{\lfloor{m \over p}\rfloor}[d|i][d|j] \]

  • 我们考虑 \(\sum\limits_{i=1}^{\lfloor{n \over p}\rfloor}\sum\limits_{j=1}^{\lfloor{m \over p}\rfloor}[d|i][d|j]\) 的实际意义,我们发现就是分着求在 \(\lfloor {n \over p} \rfloor\) 中能整除 \(d\) 的个数,显然可以转化为 \(\lfloor{{\lfloor{n \over p}\rfloor} \over d}\rfloor\),可以举例算一下,\(m\) 同理,所以原式化为:

\[\sum\limits_{p}\sum\limits_{d}\mu (d) \lfloor{{\lfloor{n \over p}\rfloor} \over d}\rfloor \times \lfloor{{\lfloor{m \over p}\rfloor} \over d}\rfloor \]

  • 这个柿子很简了,但是我们还是不好做,先介绍一个定理方便化简:

\[\lfloor{{\lfloor{a \over b}\rfloor} \over c}\rfloor = \lfloor{a \over {b \times c}}\rfloor \]

  • 知道了这个,后面的柿子就化为:

\[\lfloor{n \over {p \times d}}\rfloor \times \lfloor{m \over {p \times d}}\rfloor \]

  • 就有原式:

\[\sum\limits_{p}\sum\limits_{d}\mu (d) \lfloor{n \over {p \times d}}\rfloor \times \lfloor{m \over {p \times d}}\rfloor \]

  • 我们令 \(T = p \times d\),就有:

\[\sum\limits_{T} \mu ({T \over p}) \lfloor{n \over T}\rfloor \times \lfloor{m \over T}\rfloor \]

  • 前面的柿子可以预处理出来,设一个数组为 \(k\),令 \(k_T = \sum\mu ({T \over k})\) 就行,后面的柿子可以用整除分块在 \(O(\sqrt n)\) 的复杂度内算出来
  • 对了,后面的需要用高阶整除分块,这个怎么做呢,可以看看这篇的例题 \(5\),挺清晰msjing就不讲了,你非要看msjing也没法,在这是msjing的
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e7+10;
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<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m;
int prime[maxn],np[maxn];
int mo[maxn],k[maxn],s[maxn];
void getmo(int n)
{
    // cerr << "---oh i am here---" << endl;
    mo[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!np[i])
            prime[++prime[0]]=i,mo[i]=-1;
        for (int j=1;j<=prime[0] && i*prime[j]<=n;j++)
        {
            np[i*prime[j]]=1;
            if (!(i%prime[j]))
            {
                mo[i*prime[j]]=0;
                break;
            }
            mo[i*prime[j]]=mo[i]*(-1);
        }
    }
    // cerr << "---i can run here---" << endl;
    for (int i=1;i<=prime[0];i++)
            for (int j=1;j*prime[i]<=n;j++)
                k[j*prime[i]]+=mo[j];
    for (int i=1;i<=n;i++) s[i]=s[i-1]+k[i];
}
int get(int x,int y)
{
    int res=0;
    for (int i=1,j=0;i<=x;i=j+1)
        j=min(x/(x/i),y/(y/i)),res+=(x/i)*(y/i)*(s[j]-s[i-1]);
    return res;
}
signed main()
{
    // cerr << "--------------" << endl;
    getmo(maxn-10);
    // cerr << "--------------" << endl;
	int T=read();
    while (T--)
    {
        n=read(),m=read();
        if (n>m) swap(n,m);
        // cerr << "-------------------" << endl;
        printf("%lld\n",get(n,m));
        // cerr << "---------------------" << endl;
    }
	return 0;
}

后记

  • msjing就学到这了,更难的以后再说
  • 谢谢ybc喵
posted @ 2026-08-14 21:42  msjing  阅读(15)  评论(3)    收藏  举报