数论学习笔记(二):数论函数

数论函数

If for every value of x,

y has a completely determined corresponding value,

then y is a function of x.

Johann Dirichlet

基本概念

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

加性函数:若对于任意正整数 a,bgcd(a,b)=1,都有 f(ab)=f(a)+f(b),则称 f 为加性函数。

若对于任意正整数 a,b,都有 f(ab)=f(a)+f(b),则称 f 为完全加性函数。

积性函数、完全积性函数:见数论学习笔记(一):同余相关

可以发现,对于积性函数 ff(n)=f(1)f(n),因此 f(1) 永远为 1

唯一分解定理:任何大于 1 的正整数都可以唯一分解成有限个质数的乘积,即 n=i=1npicipi 互不相等且均属于素数集 P)。

常见数论函数

设正整数 n=i=1npici,那么可以给出以下数论函数:

  • 单位函数:ε(n)=[n=1],显然,它是完全积性函数。

  • 常量函数:1(n)=1,也很显然,它是完全积性函数。

  • 幂函数:idk(n)=nk,当 n=1 时记为 id(n),此时它被称作恒等函数,它是完全积性函数。

证:对于正整数 n,midk(n)idk(m)=nkmk=(mn)k=idk(nm)

  • 除数函数:σk(n)=dndk,当 n=0 时记为 d(n)τ(n),此时它表示 n 的因子个数;当 n=1 时记为 σ(n),此时它表示 n 的因数和,他是积性函数。

证:对于正整数 m,n,当 n=m=1 时显然成立。

n,m 不全为 1gcd(n,m)=1 时,σk(n)σk(m)=d1nd1kd2md2k=d1nd2md1kd2k=d1nd2m(d1d2)k

d1nd2m

n=ad1m=bd2

nm=abd1d2

(d1d2)(nm)

gcd(n,m)=1

d1d2d1d2

d1d2 恰好遍历了 nm 的所有因子。

原式 =dnmdk=σk(nm)

计算式

σk(n)={i=1m(ci+1):k=0i=1mpik(ci)+11pi1:k>0

证:当 k=0 时,它表示 n 的因数个数,考虑每个因数中每个质因子出现了多少遍,每个质因子都可以出现 0ci 次,根据乘法原理可以求出。

k>0 时,还是考虑每个因数中每个质因子出现了多少遍,每个质因子都可以为答案贡献 j=0cipijk

σ 是积性函数。

σk(n)=i=1mj=0cipijk

对它进行等比数列求和,就可以求出等式。

  • 欧拉函数:φ(n)=i=1n[gcd(i,n)=1],它表示小于等于 n 且与 n 互质的数的个数,它的基本性质详见数论学习笔记(一):同余相关

  • 本质不同的质因子个数函数:ω(n)=pP[pn],它表示一个数本质不同的质因子个数 (有点废话),他是加性函数

证:对于正整数 n,mgcd(n,m)=1

n=1 时,ω(1)=pP[p1]=0

n>1 时,

ω(nm)=pP[pnm]=pP[pn][pm]=pP[pn]+pP[pm]pP[pn][pm]

考虑容斥原理,则有:

=pP[pn]+pP[pm]pP[pgcd(n,m)]=pP[pn]+pP[pm]pP[p1]=pP[pn]+pP[pm]=ω(n)+ω(m)

  • 莫比乌斯函数:

μ(n)={1:n=10:d>1,d2n(1)ω(n):otherwise

它是积性函数。

证:当 n=1 时,μ(n)=1

n,m 中有一个是 1,假设 n1

μ(nm)=μ(m)=1×μ(m)=μ(n)μ(m)

d>1,d2n

d2nm

μ(nm)=0=0×μ(m)=μ(n)μ(m)

其他情况中 μ(n)μ(m)=(1)ω(n)(1)ω(m)=(1)ω(n)+ω(m)

ω 是加性函数

原式 =(1)ω(nm)=μ(nm)

  • 总质因数个数函数: Ω(n)=pPpαn1(α>0),它表示一个数质数幂因子的个数,它是完全加性函数。

证:首先,当 piP 时,Ω(pi)pPpαpi1=1

Ω(npi)=pPpαnpi1=pPppipαnpi1+piαnpi1=pPppipαn1+(piαn1+1)=(pPppipαn1+piαn1)+1=pPpαn1+1=Ω(n)+Ω(pi)

  • 刘维尔函数:λ(n)=1Ω(n),显然,它是完全积性函数。

狄利克雷卷积

定义

两个数论函数 fg 的狄利克雷卷积 (fg)(n)=dnf(d)g(nd)=xy=nf(x)g(y)

性质

  1. 交换律:fg=gf

证:(fg)(n)=dnf(d)g(nd)

考虑到 dn 的因子,于是 d=nd,那么 nd=d,则原式 =dng(d)f(nd)=gf

  1. 结合律:(fg)h=f(gh)

证:

((fg)h)(n)=dn(idf(i)g(di))h(nd)=inf(i)(d=kidng(di)h(nd))=inf(i)(knig(k)h(nik))=f(gh)

  1. 分配律:(f+g)h=fh+gh

证:

((f+g)h)(n)=dn(f(d)+g(d))h(nd)=dnf(d)h(nd)+dng(d)h(nd)=fh+gh

  1. 单位元:fε=εf=f

证:εf=dn[d=1]f(nd)=f(n)

现在我们可以发现,ε 是狄利克雷卷积的单位元,因此对于一个积性函数 f,若存在积性函数 g 使得 fg=ε,则 gf 的狄利克雷逆。

  1. 积性函数 f 存在狄利克雷逆当且仅当 f(1)0

证:设 fg=ε,若 f(1)=0,则 f(1)g(1)=0,但是 f(1)g(1)=[1=1]=1,矛盾,则此时不存在狄利克雷逆。

假设 f(1)0,则 dnf(d)g(nd)=0,于是 g(n)=dndng(d)f(nd)f(1),因此可以递推出 g

  1. 积性函数的狄利克雷逆仍是积性函数。

证:考虑数学归纳法。

fg=ε,先考虑边界情况,因为 g(1)=1f(1)=1,因此 g(1)g(1)=g(1),边界情况成立。

对于 n,m>1gcd(n,m)=1,假设对于所有 xy<nmgcd(x,y)=1,都有 g(x)g(y)=g(xy),现在来证明 g(m)g(n)=g(mn)

根据上一条推出的递推式,g(nm)=dnmdnmg(d)f(nmd)

由于此时 d<nm,于是原式会变成 an,bm,abnmg(a)g(b)f(na)f(nb),将式子加上 f(1)2g(n)g(m) 再减去,可以得到 f(1)2g(n)g(m)ang(a)f(na)bmg(b)f(mb)

后方的式子 =ang(a)f(na)[m=1],由于 m>1,因此此项为 0,于是 g(nm)=f(1)2g(n)g(m)=g(n)g(m),符合数学归纳法,证明成立。

  1. 两个积性函数的狄利克雷卷积仍是积性函数。

证:设 fg=h,若 gcd(n,m)=1,则

h(n)h(m)=(d1nf(d1)g(nd1))(d2nf(d2)g(nd2))=d1d2nf(d1)f(d2)g(nd1)g(nd2)=d1d2nf(d1d2)g(nd1d2)=dnf(d)g(nd)=h(nm)

数论函数之间的关系

  1. μ1=ε

证:

(μ1)(n)=dnμ(d)1(nd)=dnμ(d)=i=0ω(n)(ω(n)i)(1)i=[1+(1)]k

[1+(1)]k={1:k=00:k0

[1+(1)]k=[k=0]=[n=1]=ε(n)

  1. φ1=id

证:我们已经在数论学习笔记(一):同余相关中用一个比较取巧的方法证明了这个式子,现在我们再用狄利克雷卷积来证明。

因为 φid 都是积性函数,因此,只用证明该式子在质数幂 pk 处成立,就可以通过唯一分解定理,将若干个 pk 乘起来得到其它正整数。

(φ1)(pk)=dpkφ(d)=i=0kφ(pi)=1+i=1kpipi1=pk

  1. μid=φ

证:由于我们知道了 φ1=id,我们在两边同时再卷上一个 μ,可以得到 φ1μ=idμ,又因为 1μ=ε,而 εφ=φ,因此 μid=φ

  1. 11=d=σ0

证:(11)(n)=dn1=d(n)

  1. 1idk=σk

证:(1idk)(n)=dndk=σk(n)

点积

整除分块

这是数论题最常见的做法。组合数推式子一般推到所有因子可以预处理,而数论推式子一般就推到所有因子可以求前缀和或可以整除分块。

考虑一共有多少个不同的 nx,分以下两种情况讨论:

  • xn,则不同的 x 只有 O(n) 种,于是不同的 nx 也只有 O(n) 种。

  • x>n,则 nxn,因此 nx 也只有 O(n) 种。

于是我们就可以很容易求出这 O(n) 种不同的 nx

for(int l = 1, r; l <= n; l = r + 1)
    r = n / (n / l);

于是对于每个 x[l,r],都有 nx=nl

莫比乌斯反演

首先,莫比乌斯反演是一类特殊的子集反演,详见组合数学学习笔记(二):鸽巢原理、容斥及反演。莫比乌斯反演式子如下:

f(n)=dng(d)g(n)=dnμ(d)f(nd)

证:

dnμ(d)f(nd)=dnμ(nd)f(d)=dnμ(nd)kdg(k)=kndnkμ(nkd)g(k)=kndnkμ(d)g(k)

现在可以发现,由于 μ1=ε,那么 dnkμ(d)=[nk=1],于是只有 kn 时值为 1,那么原式就等于 g(n),证明成立。

将莫比乌斯反演与子集反演的式子进行对比:

f(n)=dng(d)g(n)=dnμ(d)f(nd)f(S)=TSg(T)g(S)=TS(1)|S||T|f(S)

不难发现二者有一些相似之处:考虑记 Sn 的所有素因数的多重集,那么枚举因数 dn 实际上就是枚举了一个子多重集 TS。因此莫比乌斯反演的实质就是子多重集反演,而 μ 就是这个反演的系数。

例题一

解:非常经典的莫比乌斯反演的题目,我们要求 i=1nj=1ngcd(i,j)

首先我们先改变枚举方式,先枚举最大公约数 d,再枚举任意两个数的最大公约数是否为 d,那么原式变成 d1di=1nj=1n[gcd(i,j)=d]

考虑到若 gcd(i,j)=d,那么 gcd(id,jd)=1,于是原式就变成了 d1di=1ndj=1nd[gcd(i,j)=1]

现在可以发现 [gcd(i,j)=1] 就是 ε,因为 μ1=ε,于是式子再次化成 d1di=1ndj=1ndkgcd(i,j)μ(k)

考虑到 kgcd(i,j)kikj,于是原式 =d1dk1μ(k)i=1nd[ki]j=1nd[kj]=d1dk1μ(k)ndk2

由于 ndk=ndk,于是设 T=dk,那么原式就会变成 T=1nnT2(dk=Tμ(k)d),注意到 dk=Tμ(k)d=μid=φ,于是这个式子最终化简成 T=1nnT2φ(T),于是求出 φ 的前缀和,就可以用整除分块来做这道题目了。

完整代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e6 + 9;
int vis[N], prime[N], phi[N], sum[N], n, ans;
void get_phi(){
	phi[1] = 1;
	int cnt = 0;
	for(int i = 2; i < N; i++){
		if(!vis[i]){
			vis[i] = i;
			prime[cnt++] = i;
			phi[i] = i - 1;
		}
		for(int j = 0; j < cnt && i * prime[j] < N; j++){
			vis[i * prime[j]] = prime[j];
			if(i % prime[j] == 0){
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}	
			phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}
	for(int i = 1; i < N; i++)
		sum[i] = sum[i - 1] + phi[i];
}
signed main(){
	get_phi();
	scanf("%lld", &n);
	for(int l = 1, r; l <= n; l = r + 1){
		r = n / (n / l);
		ans += (n / l) * (n / l) * (sum[r] - sum[l - 1]);
	}
	printf("%lld", ans);
	return 0;
}

例题二

与上一道题一样,只是这次要求的式子变成了 T=1nnTmTφ(T),于是我们需要对 mn 都整除分块,最后取 min 就可以了。

注意最后这道题要 ×2 再减 n×m

完整代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e6 + 9;
int vis[N], prime[N], phi[N], sum[N], n, m, ans;
void get_phi(){
	phi[1] = 1;
	int cnt = 0;
	for(int i = 2; i < N; i++){
		if(!vis[i]){
			vis[i] = i;
			prime[cnt++] = i;
			phi[i] = i - 1;
		}
		for(int j = 0; j < cnt && i * prime[j] < N; j++){
			vis[i * prime[j]] = prime[j];
			if(i % prime[j] == 0){
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}	
			phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}
	for(int i = 1; i < N; i++)
		sum[i] = sum[i - 1] + phi[i];
}
signed main(){
	get_phi();
	scanf("%lld%lld", &n, &m);
	for(int l = 1, r; l <= n && l <= m; l = r + 1){
		r = min(n / (n / l), m / (m / l));
		ans += (n / l) * (m / l) * (sum[r] - sum[l - 1]);
	}
	printf("%lld", ans * 2 - n * m);
	return 0;
}

例题三

这道题要求 i=1n1j=1n1[gcd(i,j)=1]+2,我们来求他的一般形式 i=1nj=1m[gcd(i,j)=1]

还是大力推式子:

i=1nj=1m[gcd(i,j)=1]=i=1nj=1mdgcd(i,j)μ(d)=d1μ(d)i=1n[di]j=1m[dj]=d1μ(d)ndmd

依然先求出 μ 的前缀和,然后就可以用整除分块做了。

完整代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 9;
int vis[N], prime[N], mu[N], sum[N], ans;
void get_mu(){
	mu[1] = 1;
	int cnt = 0;
	for(int i = 2; i < N; i++){
		if(!vis[i]){
			vis[i] = i;
			prime[cnt++] = i;
			mu[i] = -1;
		}
		for(int j = 0; j < cnt && i * prime[j] < N; j++){
			vis[i * prime[j]] = prime[j];
			if(i % prime[j] == 0)
				break;
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < N; i++)
		sum[i] = sum[i - 1] + mu[i];
}
int main(){
	get_mu();
	int n;
	scanf("%d", &n);
	n--;
	if(!n){
		printf("0\n");
		return 0;
	}
	for(int l = 1, r; l <= n; l = r + 1){
		r = n / (n / l);
		ans += (n / l) * (n / l) * (sum[r] - sum[l - 1]);
	}
	printf("%d", ans + 2);
	return 0;
}

例题四

这题变成求 i=1nj=1mlcm(i,j),由于 lcm(i,j)=ijgcd(i,j),于是我们求的变成了 i=1nj=1mijgcd(i,j)

依然枚举最大公因数 d,于是要求 d0di=1nj=1m[gcd(i,j)=d]ijd,由于若 gcd(i,j)=d,那么 didj,那么将 ij 的上界都除以 d,那么原式 =d1di=1ndj=1md[gcd(i,j)=1]ij,对 ndmd 整除分块后,就只用考虑 i=1nj=1m[gcd(i,j)=1]ij 怎么求。

S(n)=i=1ni=n(n+1)2,依然大力推式子:

i=1nj=1m[gcd(i,j)=1]ij=i=1nj=1mijkgcd(i,j)μ(k)=i=1nj=1m(ak)(bk)kgcd(i,j)μ(k)=k=1nμ(k)k2i=1ndj=1mdij=k=1nμ(k)k2S(nd)S(md)

现在若能预处理积性函数 x2μ(x)的前缀和,再套一个整除分块就可以做了,不过现在是一个整除分块套整除分块的形式,复杂度为 1nxdx=O(n34),不能通过此题,于是继续化简。

我们将 i=1nj=1m[gcd(i,j)=1]ij 化简后的式子代回原式,可以得到 d1dk=1ndμ(k)k2S(ndk)S(mdk)

T=dk,于是原式 =T=1nS(nT)S(mT)kd=Tμ(k)k2d,可以发现式子 kd=Tμ(k)k2dμ(k)k2 是积性函数(狄利克雷卷积性质 7),而 kid(k),两个积性函数的点积也是积性函数,因此这也是积性函数,可以线性筛筛出,于是 O(n) 预处理 O(n) 计算即可通过此题。

完整代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e7 + 9, MOD = 20101009;
int vis[N], prime[N], sum[N], f[N], ans, n, m;
void get_f(){
	f[1] = 1;
	int cnt = 0;
	for(int i = 2; i < N; i++){
		if(!vis[i]){
			vis[i] = i;
			prime[cnt++] = i;
			f[i] = 1 - i + MOD;
		}
		for(int j = 0; j < cnt && i * prime[j] < N; j++){
			vis[i * prime[j]] = prime[j];
			if(i % prime[j] == 0){
				f[i * prime[j]] = f[i];
				break;
			}	
			f[i * prime[j]] = f[i] * f[prime[j]] % MOD;
		}
	}
	for(int i = 1; i < N; i++)
		sum[i] = (sum[i - 1] + i * f[i] % MOD) % MOD;
}
signed main(){
	get_f();
	scanf("%lld%lld", &n, &m);
	if(n > m)
		swap(n, m);
	for(int l = 1, r; l <= n && l <= m; l = r + 1){
		r = min(n / (n / l), m / (m / l));
		ans = (ans + ((n / l) * (n / l + 1) / 2 % MOD) * ((m / l) * (m / l + 1) / 2 % MOD) % MOD * (sum[r] - sum[l - 1]) % MOD + MOD) % MOD;
	}
	printf("%lld", ans);
	return 0;
} 

例题五

这真的是基础练习题吗?

线性筛积性函数与应用

高级筛法

这两个算法都可以快速求积性函数的前缀和。

杜教筛

对于一个积性函数 f,我们要求 S(n)=i=1nf(i)

我们再找到一个积性函数 g,求出它与 f 的狄利克雷卷积 fg=dnf(n)g(nd),我们求出这个函数的前缀和 i=1n(fg)(i)=i=1ndig(d)f(id)

交换求和顺序,先枚举 d,由于 di,因此原式再次变成 d=1ng(d)i=kdinf(id)=d=1ng(d)i=kdinf(k),由于 k 最多达到 nd,于是原式再次变成 d=1ng(d)i=1ndf(i)=i=1ng(i)S(ni)

由于 S(n)=g(1)S(n),于是 S(n) 可以写成 i=1ng(i)S(ni)i=2ng(i)S(ni)

我们令 h=fg,于是 S(n)=i=1nh(i)i=2ng(i)S(ni),于是,如果我们能快速求出 hg 的前缀和,就可以通过整除分块来算出 S(n)

具体地,由于不同的 i,一共有 n 个不同的 ni,于是我们通过记忆化搜索的方法,不断递归求出 S(ni),那么时间复杂度就为 O(n)+k=1nO(k)+k=2nO(nk)=O(0n(x+nx)dx)=O(n34)

考虑到 n 较小的时候可以直接通过线性筛筛出答案,不需要递归下去。假设我们将前 m 个数预处理了,那么时间复杂度会变成 O(m)+k=1nmO(nk)=O(O(m)+0nmnxdx)=O(O(m)+nm),通过均值不等式可以知道当 m=n23 时时间复杂度最优,为 O(n23)

以洛谷的杜教筛模板为例。

我们来试着筛出 φμ 的前缀和。

注意到 μ1=ε,带入杜教筛的式子中,可以得到 S(n)=1i=2n1(i)S(ni),由于 1(i) 的前缀和就是 i,于是可以用杜教筛求解。

对于函数 φ 有两种求法:

法1

i=1nφ(i)=i=1nj=1n[gcd(i,j)=1]。这个之前求过了,答案为 d1μ(d)nd2,于是可以先筛 μ 再转化为 ϕ

法二

注意到 φ1=id,将其带入杜教筛的式子中可以得到 S(n)=n(n+1)2i=2n1(i)S(ni),依然可以通过杜教筛来求。

完整代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e6 + 9;
int prime[N], mu[N], phi[N], sum1[N], sum2[N];
bool vis[N];
unordered_map <int, int> summu, sumphi;
void init(){
	int cnt = 0;
	vis[0] = vis[1] = 1;
	mu[1] = phi[1] = 1;
	for(int i = 2; i < N; i++){
		if(!vis[i]){
			prime[cnt++] = i;
			mu[i] = -1;
			phi[i] = i - 1;
		}
		for(int j = 0; j < cnt && i * prime[j] < N; j++){
			vis[i * prime[j]] = 1;
			if(i % prime[j]){
				mu[i * prime[j]] = -mu[i];
				phi[i * prime[j]] = phi[i] * phi[prime[j]];
			} else {
				mu[i * prime[j]] = 0;
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
		}
	}
	for(int i = 1; i < N; i++){
		sum1[i] = sum1[i - 1] + mu[i];
		sum2[i] = sum2[i - 1] + phi[i];
	}
}
int getsmu(int x){
	if(x < N)
		return sum1[x];
	if(summu[x])
		return summu[x];
	int ans = 1;
	for(int l = 2, r; l <= x; l = r + 1){
		r = x / (x / l);
		ans -= (r - l + 1) * getsmu(x / l);
	}
	return summu[x] = ans;
}
int getphi(int x){
	if(x < N)
		return sum2[x];
	if(sumphi[x])
		return sumphi[x];
	int ans = x * (x + 1) / 2;
	for(int l = 2, r; l <= x; l = r + 1){
		r = x / (x / l);
		ans -= (r - l + 1) * getphi(x / l);
	}
	return sumphi[x] = ans;
}
signed main(){
	init();
	int t;
	scanf("%lld", &t);
	while(t--){
		int n;
		scanf("%lld", &n);
		printf("%lld %lld\n", getphi(n), getsmu(n));
	}
	return 0;
}

Powerful Number

对于正整数 n,记 n 的质因数分解为
n=i=1mpieinPowerful Number(简称 PN) 当且仅当 1im,ei>1

Powerful Number 有如下性质:

  • 所有的 PN 都可以写成 a2b3 的形式;

证:若 ei 是偶数,直接合并进 a2;如果 ei 是奇数,那么 ei 至少为 3,于是先将 pi3 合并进 b3,再将剩下的合并进 a2

  • n 以内的 PNO(n) 个。

证:考虑先枚举 a2b3 中的 a1an),再枚举符合条件的 b 的数量,于是 PN 的个数约为 i=1nnx23dx=O(n)

正题

参考资料

posted @   JPGOJCZX  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示