Bootstrap

【数论】莫比乌斯函数及其反演

在学习之前,先来了解一下常见定义吧(OVO):

  • 常见数论函数分为两种:
    { 完全积性函数:对于任意 p , q ∈ N ,有 f ( p ⋅ q ) = f ( p ) ⋅ f ( q ) 积性函数:对于任意 p , q ∈ N 且 g c d ( p , q ) = 1 ,有 f ( p ⋅ q ) = f ( p ) ⋅ f ( q ) 非积性函数 \begin{cases} &完全积性函数:对于任意p,q \in N,有f(p \cdot q) = f(p) \cdot f(q)\\ &积性函数:对于任意p,q \in N且gcd(p,q) = 1,有f(p \cdot q) = f(p) \cdot f(q)\\ &非积性函数 \end{cases} 完全积性函数:对于任意p,qN,有f(pq)=f(p)f(q)积性函数:对于任意p,qNgcd(p,q)=1,有f(pq)=f(p)f(q)非积性函数
    常见的完全积性函数有:单位函数 ϵ ( n ) = [ n = = 1 ] \epsilon(n) = [n == 1] ϵ(n)=[n==1],常数函数 I ( n ) = = 1 I(n) == 1 I(n)==1,恒等函数 I d ( n ) = = n Id(n) == n Id(n)==n等;
    常见的积性函数有:莫比乌斯函数 μ ( n ) \mu(n) μ(n),欧拉函数 ψ ( n ) \psi(n) ψ(n),约数和函数 σ ( n ) = ∑ d ∣ n d \sigma(n) = \sum_{d | n} d σ(n)=dnd等。

  • 狄利克雷卷积定义:若 f ( n ) , g ( n ) f(n),g(n) f(n),g(n)均为数论函数,则 h ( n ) = f ( n ) × g ( n ) = ∑ d ∣ n f ( n ) ⋅ g ( n d ) = ∑ d ∣ n f ( n d ) ⋅ g ( n ) h(n) = f(n) \times g(n) = \sum_{d|n}f(n) \cdot g(\frac{n}{d}) = \sum_{d|n}f(\frac{n}{d}) \cdot g(n) h(n)=f(n)×g(n)=dnf(n)g(dn)=dnf(dn)g(n)
    其中 × \times ×为卷积运算,不懂卷积运算的可以去search一下,这里就不详细介绍了(QAQ)。

推论:

  • ϵ = I × μ = > [ n = = 1 ] = ∑ d ∣ n μ ( d ) \epsilon = I \times \mu => [n == 1] = \sum_{d|n}\mu(d) ϵ=I×μ=>[n==1]=dnμ(d)
  • ψ = I d × μ = > ψ ( n ) = ∑ d ∣ n μ ( d ) ⋅ ⌊ n d ⌋ \psi = Id \times \mu => \psi(n) = \sum_{d|n}\mu(d) \cdot \lfloor \frac{n}{d} \rfloor ψ=Id×μ=>ψ(n)=dnμ(d)dn

一、介绍

莫比乌斯函数及其反演同样是数论中的重要内容。


定义 μ \mu μ为莫比乌斯函数,
μ ( n ) = { 1 , n = 1 0 , n 含有平方及以上因子 ( − 1 ) k , k 为 n 的质因子个数 \mu(n) = \begin{cases} &1, n = 1\\ &0, n含有平方及以上因子\\ &(-1)^k,k为n的质因子个数\\ \end{cases} μ(n)= 1,n=10,n含有平方及以上因子(1)k,kn的质因子个数
这里的质因子个数指的是只出现过一次的质因子的个数,一旦某个质因子出现2次及以上,则 μ ( n ) = 0 \mu(n) = 0 μ(n)=0


莫比乌斯函数是积性函数,因此可根据 ϵ = I × μ \epsilon = I \times \mu ϵ=I×μ推导出 [ n = = 1 ] = ∑ d ∣ n μ ( d ) [n == 1] = \sum_{d|n}\mu(d) [n==1]=dnμ(d)
这个公式很重要,务必牢记!

莫比乌斯反演:
  若 g ( n ) = ∑ d ∣ n f ( d ) g(n) = \sum_{d|n}f(d) g(n)=dnf(d),则 f ( n ) = ∑ d ∣ n g ( d ) ⋅ μ ( n d ) = ∑ d ∣ n g ( n d ⋅ μ ( d ) f(n) = \sum_{d|n}g(d) \cdot \mu(\frac{n}{d}) = \sum_{d|n}g(\frac{n}{d} \cdot \mu(d) f(n)=dng(d)μ(dn)=dng(dnμ(d),反之亦然。

二、莫比乌斯函数的算法求解

由于莫比乌斯函数的定义和性质,可利用欧拉线性筛求出 μ ( x ) \mu(x) μ(x)时间复杂度为 O ( n ) O(n) O(n)

//求n以内的莫比乌斯函数
int mu[N];
void mobius(int n)
{
	bitset<N> vis;//素数筛子
	vector<int> ps;//素数数组
	vis[0] = vis[1] = true;
	mu[1] = 1;
	for(int i = 2;i <= n;i++)
	{
		if(!vis[i])ps.push_back(i),mu[i] = -1;
		
		//枚举素数表
		for(int j = 0;j < (int)ps.size() && i * ps[j] <= n;j++)
		{
			vis[i * ps[j]] = true;//筛掉
			if(i % ps[j] == 0)
			{
				mu[i * ps[j]] = 0;
				break;  
			} 
			mu[i * ps[j]] = -mu[i];
		}
	}
}

三、例题

1、【模板】莫比乌斯反演
题目描述 给定三个整数 x , y , d , 求解: ∑ i = 1 x ∑ j = 1 y [ g c d ( i , j ) = = d ] 。其中包含 T ( 1 ≤ T ≤ 1000 )组测试样例,每组测试样例三个整数 x , y , d ( 1 ≤ ∑ x , ∑ y , d ≤ 1 0 5 )。 给定三个整数x,y,d,求解:\sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j) == d]。其中包含T(1 \leq T \leq 1000)组测试样例,每组测试样例三个整数x,y,d(1 \leq \sum x,\sum y,d \leq 10^5)。 给定三个整数x,y,d,求解:i=1xj=1y[gcd(i,j)==d]。其中包含T1T1000)组测试样例,每组测试样例三个整数x,y,d1x,y,d105)。

题目分析:

由题给公式可以推出 ∑ i = 1 ⌊ x d ⌋ ∑ j = 1 ⌊ y d ⌋ [ g c d ( i , j ) = = 1 ] \sum_{i=1}^{\lfloor \frac{x}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{y}{d} \rfloor}[gcd(i,j) == 1] i=1dxj=1dy[gcd(i,j)==1],由莫比乌斯反演可将公式推导为

∑ i = 1 ⌊ x d ⌋ ∑ j = 1 ⌊ y d ⌋ ∑ k ∣ g c d ( i , j ) μ ( k ) \sum_{i=1}^{\lfloor \frac{x}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{y}{d} \rfloor} \sum_{k|gcd(i,j)}\mu(k) i=1dxj=1dykgcd(i,j)μ(k)

= ∑ k = 1 m i n ( x d , y d ) μ ( k ) ∑ i = 1 ⌊ x d ⌋ [ k ∣ i ] ∑ j = 1 ⌊ y d ⌋ [ k ∣ j ] = \sum_{k=1}^{min(\frac{x}{d},\frac{y}{d})}\mu(k) \sum_{i=1}^{\lfloor \frac{x}{d} \rfloor}[k|i]\sum_{j=1}^{\lfloor \frac{y}{d} \rfloor}[k|j] =k=1min(dx,dy)μ(k)i=1dx[ki]j=1dy[kj]

= ∑ k = 1 m i n ( x d , y d ) μ ( k ) ⋅ ⌊ ⌊ x d ⌋ k ⌋ ⋅ ⌊ ⌊ y d ⌋ k ⌋ = \sum_{k=1}^{min(\frac{x}{d},\frac{y}{d})} \mu(k)\cdot \lfloor \frac{\lfloor \frac{x}{d} \rfloor}{k} \rfloor \cdot \lfloor \frac{\lfloor \frac{y}{d} \rfloor}{k} \rfloor =k=1min(dx,dy)μ(k)kdxkdy

题解

//Code Here.
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int N = 1e5 + 9;

//求n以内的莫比乌斯函数
int mu[N];
void mobius(int n)
{
	bitset<N> vis;//素数筛子
	vector<int> ps;//素数数组
	mu[1] = 1;
	for(int i = 2;i <= n;i++)
	{
		if(!vis[i])ps.push_back(i),mu[i] = -1;
		
		//枚举素数表
		for(int j = 0;j < (int)ps.size() && i * ps[j] <= n;j++)
		{
			vis[i * ps[j]] = true;//筛掉
			if(i % ps[j] == 0)
			{
				mu[i * ps[j]] = 0;
				break;  
			} 
			mu[i * ps[j]] = -mu[i];
		}
	}
}

signed main()
{
    mobius(N - 1);//预处理,求出u(x)
    int t;cin >> t;
    while(t--)
    {
        int x,y,d;cin >> x >> y >> d;
        int res = 0;
        int n = x / d,m = y / d;
        for(int i = 1;i <= min(n,m);i++)
        {
            res += mu[i] * (n / i) * (m / i);
        }
        cout << res << '\n';
    }
}

2、公约数的和

题目描述 给定 n ,求解 ∑ i = 1 n ∑ j = i + 1 n g c d ( i , j ) ,结果对 1 0 9 + 7 取模。其中包含 T ( 1 ≤ T ≤ 1000 )组测试样例,每组测试样例一个整数 n ( 1 ≤ n ≤ 1 0 6 )。 给定n,求解\sum_{i=1}^{n} \sum_{j=i+1}^{n} gcd(i,j),结果对10^9 + 7取模。其中包含T(1 \leq T \leq 1000)组测试样例,每组测试样例一个整数n(1 \leq n \leq 10^6)。 给定n,求解i=1nj=i+1ngcd(i,j),结果对109+7取模。其中包含T1T1000)组测试样例,每组测试样例一个整数n1n106)。

题目分析:此题稍难,只给出简单公式推导(QAQ)

∑ i = 1 n ∑ j = i + 1 n g c d ( i , j ) = ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) − ∑ i = 1 g c d ( i , i ) 2 \sum_{i=1}^{n} \sum_{j=i+1}^{n} gcd(i,j) = \frac{\sum_{i=1}^{n} \sum_{j=1}^{n} gcd(i,j) - \sum_{i=1}gcd(i,i)}{2} i=1nj=i+1ngcd(i,j)=2i=1nj=1ngcd(i,j)i=1gcd(i,i)

∑ i = 1 n ∑ j = 1 n g c d ( i , j ) = ∑ t = 1 n ψ ( t ) ⋅ ( ⌊ n t ⌋ ) 2 \sum_{i=1}^{n} \sum_{j=1}^{n} gcd(i,j) = \sum_{t=1}^{n}\psi(t) \cdot (\lfloor \frac{n}{t} \rfloor)^2 i=1nj=1ngcd(i,j)=t=1nψ(t)(⌊tn)2
题解

//Code Here.
#include <bits/stdc++.h>
using namespace std;
#define int long long
using ll = long long;
const int p = 1e9 + 7;

const int N = 1e6 + 9;

ll qmi(ll a,ll b)//快速幂((a^b)modc)
{
	ll res = 1;
	while(b)
	{
		if(b & 1)res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
int mo(int x){return (x % p + p) % p;}

//欧拉筛求n以内的欧拉函数
ll phi_euler[N];
void _phi_euler(int n)
{
	bitset<N> vis;//素数筛子
	vector<int> ps;//素数数组
	phi_euler[1] = 1;
	for(int i = 2;i <= n;i++)
	{
		if(!vis[i])ps.push_back(i),phi_euler[i] = i - 1;
		
		//枚举素数表
		for(int j = 0;j < (int)ps.size() && i * ps[j] <= n;j++)
		{
			vis[i * ps[j]] = true;//筛掉
			if(i % ps[j] == 0)
			{
				phi_euler[i * ps[j]] = phi_euler[i] * ps[j];
				break;  
			} 
			phi_euler[i * ps[j]] = phi_euler[i] * phi_euler[ps[j]];
		}
	}
    for(int i = 1;i <= n;i++)phi_euler[i] = mo(phi_euler[i] + phi_euler[i - 1]);
}

signed main()
{
    _phi_euler(N - 1);
    int inv2 = qmi(2,p - 2);
    int t;cin >> t;
    while(t--)
    {
        int n;cin >> n;
        int res = 0;
        for(int l = 1,r = 1;l <= n;l = r + 1)
        {
            r = min(n,n / (n / l));
            res = mo(res + mo(phi_euler[r] - phi_euler[l - 1]) * mo(n / l) % p * mo(n / l) % p);
        }
        int resn = mo(n * (n + 1) % p * inv2);
        cout << mo(mo(res - resn) * inv2) << '\n';
    }
    
}
;