简介
数论是纯粹数学的分支之一,主要研究整数的性质。整数可以是方程式的解。有些解析函数中包括了一些整数、质数的性质,透过这些函数也可以了解一些数论的问题。
数论被高斯成为“数学皇后”,是一大必不可少的分支。
OI 里的解析数论主要研究的是狄利克雷卷积、积型函数、筛法。下面将会给出本博客介绍的东西:
- 常见积性函数(欧拉函数和莫比乌斯函数)。
- 线性筛。
- 狄利克雷卷积。
- 杜教筛和 min25 筛。
- 欧拉反演。
- 莫比乌斯反演。
在本文中,若无特殊说明,我们规定 p 是质数。
积性函数
若对于一个函数 f 满足对于两个互质的数 a,b 满足 f(a)f(b)=f(ab),则 f 为积性函数。若其对于任意两个数 a,b 满足 f(a)f(b)=f(ab),则 f 为完全积性函数。
积性函数有很多:φ,μ,id……下面将会选取两个典型 φ,μ 来介绍。
欧拉函数
欧拉函数 φ(n) 表示 [1,n] 的正整数中,与 n 互质的数的个数。
欧拉函数有一些很美妙的性质。首先,根据定义,结合常识,因为质数与小于它的任何正整数都互质,所以有 φ(p)=p−1。类似的,对于一个质数的幂,可以得到 φ(pk)=pk−pk−1。这个式子做一个简单的变形,就可以得到 φ(p)=pk(1−p1)。
φ(n)=n∏(1−pi1)
证明
把 n 其都可以拆分成 ∏piki 的形式。由于 φ 是积性函数,p1k1,p2k2⋯ 显然互质,因此有:
φ(n)=∏φ(piki)=∏[piki(1−pi1)]
前者乘起来是 n,因此:
φ(n)=n∏(1−pi1)
当然还有一些比较有趣的性质,例如如果你算一下 n 所有因数的欧拉函数值然后加起来。发现其刚好等于 n。
d∣n∑φ(d)=n
证明
写下分母为 n 的所有分数 ni。然后给每个数约分。约分后的分数里,分母就是 n 的所有因数,分子就其与其互质的数。
例如 61,62,63,64,65,66,约分变成 61,31,21,32,65,11。分母为 6,3,2,1 的分别有 2,2,1,1 个,而 φ(6)=2,φ(3)=2,φ(2)=1,φ(1)=1。
如果求出 [1,n] 中与 n 互质的数字加起来?
d=1∑nd[gcd(n,d)=1]=2nφ(n)
证明
若 gcd(n,d)=1,则根据辗转相除法,gcd(n−i,n)=1。因此与 n 互质的数可以两两配对,每对的和都是 n,一共有 2φ(n) 对。
莫比乌斯函数
莫比乌斯函数 μ(n) 表示:若 n 存在除 1 外的完全平方数因子,则 μ(n)=0,否则,若 n 有 m 个不同的质因子,μ(n)=(−1)m。
和欧拉函数类似,探究一下它的性质,例如如果你求出 n 的所有质因子的莫比乌斯函数值,然后加起来?
d∣n∑μ(d)=[n=1]
证明
μ 为 0 的数对答案没有影响。考虑对于有 k 个不同质因数的数字 n,由其中 r 个相乘得到的就是 μ 不为 0 的因数个数:
d∣n∑μ(d)=i=0∑k(−1)i(ik)
二项式定理:
i=0∑k(−1)i(ik)=(1−1)k=0k
当 n=1 时,k=n−1=0,原式为 1,否则为 0。
莫比乌斯函数还有很多性质,由于涉及到后文的知识,此处先不提及。
线性筛
线性筛可以在 O(n) 的时间内求出 [1∼n] 的素数,和 [1∼n] 每个数的某个积性函数值。其关键点在于,枚举筛子 i 和质数 j 时,如果 j 是 i 的最小质因子就不需要接着筛了。因为 j 取更大时 ij 会被另外一个 i 更新到。
因此想要筛出一个积性函数 f,必须要知道的是:
- f(1) 的值和 f(p) 的值。
- f(ij) 如何计算,其中 j 是 i 的最小质因子。
例如线性筛欧拉函数,需要知道 φ(ij)(j 是 i 最小质因子)怎么算,手玩可以发现为 φ(i)j。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| inline void GetPhi(int n){ memset(isPrime, 1, sizeof(isPrime)); isPrime[1] = 0, phi[1] = 1; for(register int i = 2; i <= n; ++i){ if(isPrime[i]){ Prime[++cnt] = i, phi[i] = i - 1; } for(register int j = 1; j <= cnt && i * Prime[j] <= n; ++j){ isPrime[ i * Prime[j] ] = 0; if(i % Prime[j] == 0){ phi[ i * Prime[j] ] = phi[i] * Prime[j]; break; } else phi[ i * Prime[j] ] = phi[i] * phi[Prime[j]]; } } }
|
再例如莫比乌斯函数,显然 μ(ij)(j 是 i 的最小质因子)为 0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void GetMu(int n){ memset(isPrime, 1, sizeof(isPrime)); isPrime[1] = 0, mu[1] = 1; for(register int i = 2; i <= n; ++i){ if(isPrime[i]){ Prime[++cnt] = i, mu[i] = -1; } for(register int j = 1; j <= cnt && i * Prime[j] <= n; ++j){ isPrime[ i * Prime[j] ] = 0; if(i % Prime[j] == 0){ mu[i*prime[j]]=0; break; } else mu[ i * Prime[j] ] = mu[i] * mu[Prime[j]] } } }
|
高级筛法
狄利克雷卷积
对于三个函数 f,g,h,若 h(n)=∑d∣nf(d)g(dn),则记 f∗g=h,h 为 f,g 的狄利克雷卷积。
狄利克雷卷积满足交换律,结合律,分配率。两个积性函数的狄利克雷卷积也是积性函数。
定义单位元函数 ε(n)=[n=1],显然对于任意函数有 ε∗f=f。定义函数 1(n)=1 和 id(n)=n。显然这些函数都是积性函数,而且都是完全积性函数。
考虑 1 函数有什么性质,容易发现:
f∗1=d∣n∑f(d)
后面这一块前面提到过,结合欧拉函数和莫比乌斯函数的性质:
μ∗1=d∣n∑μ(d)=[n=1]=ε
φ∗1=id
杜教筛
拥有这样的前置知识之后,考虑下面的问题:
给定 n,求:
ans1=i=1∑nφ(i)
ans2=i=1∑nμ(i)
多组数据,T≤10,n<231。
因为 n 的范围很大,显然欧拉筛是筛不过去的。就算能卡进去别忘了还要求一波前缀和。而杜教筛就可以以一个较优秀的复杂度,处理这些函数的前缀和。
设要求的前缀和函数 S(n)=∑i=1nf(i)。找一个积性函数 g,则 f∗g 的前缀和为:
sum=i=1∑n(f∗g)(i)=i=1∑nd∣i∑f(d)g(di)=d=1∑ng(d)i=1∑⌊dn⌋f(i)=d=1∑ng(d)S(⌊dn⌋)
因此有:
i=1∑n(f∗g)(i)=g(1)S(n)+d=2∑ng(d)S(⌊dn⌋)
g(1)S(n)=i=1∑n(f∗g)(i)−d=2∑ng(d)S(⌊dn⌋)
S 为我们要求的函数,现在它可以用数论分块求出。现在只需要做到快速求出 f∗g 和 g 的前缀和。注意到 φ∗1=id,μ∗1=ε,而 ε,1,id 的前缀和是显然的。因此令 g=1 即可。
实际递归时先用线性筛求出 107 以内的前缀和会少一点递归层数。杜教筛的复杂度为 O(n32)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include<bits/stdc++.h> #define int long long using namespace std; const int N=10000000; inline int read(){ int x=0;bool w=0;char c=getchar(); while(c<'0' || c>'9') w|=c=='-',c=getchar(); while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return w?-x:x; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10^48); }
int n,p[N+1],m[N+1];
map<int,int> mu,ph;
inline int Mu(int n){ if(n<=N) return m[n]; if(mu.count(n)) return mu[n]; int res=1; for(register int l=2,r;l<=n;l=r+1){ r=n/(n/l); res-=(r-l+1)*Mu(n/l); } return mu[n]=res; } inline int Phi(int n){ if(n<=N) return p[n]; if(ph.count(n)) return ph[n]; int res=n*(n+1)/2; for(register int l=2,r;l<=n;l=r+1){ r=n/(n/l); res-=(r-l+1)*Phi(n/l); } return ph[n]=res; }
bool isp[N+1]; int pr[N+1],cnt;
inline void init(int n){ memset(isp,1,sizeof(isp)),isp[1]=0; p[1]=m[1]=1; for(register int i=2;i<=n;++i){ if(isp[i]){ pr[++cnt]=i; p[i]=i-1,m[i]=-1; } for(register int j=1;j<=cnt && pr[j]*i<=n;++j){ isp[i*pr[j]]=0; if(i%pr[j]==0){ p[i*pr[j]]=p[i]*pr[j], m[i*pr[j]]=0; break; } p[i*pr[j]]=p[i]*p[pr[j]], m[i*pr[j]]=-m[i]; } } for(register int i=1;i<=n;++i) p[i]+=p[i-1],m[i]+=m[i-1]; }
signed main(){ init(N); for(register int T=read();T>=1;--T){ n=read(); write(Phi(n)),putchar(' '), write(Mu(n)),putchar('\n'); } return 0; }
|
min25 筛
对于积性函数 f 满足 f(pk)=pk(pk−1)。求 ∑i=1nf(i)。
n≤1010,答案对大质数取模。
我们发现对于这样的函数,很难找到一个 g 满足 g 和 f∗g 的前缀和比较好算。因此需要考虑另外一个筛法:min25 筛。
为了方便,设 P 为质数集。Pi 表示第 i 个质数,P0=0。不妨先考虑求出这个东西:
i=1∑nf(i)[i∈P]
设 g(n,j) 表示 [1,n] 中所有数的最小质因子里,大于 Pj 的合数的 f 的和。对 j 分类讨论:
- 若 Pj2>n,则区间 (Pj,n) 中不存在这样的合数。g(n,j)=g(n,j−1)。
- 否则,g(n,j) 与 g(n,j−1) 相比,少了最小质因子恰好为 Pj 的贡献,相当于把 Pj 的贡献提出来:g(n,j)=g(n,j−1)−f(Pj)g(Pjn,j)。然而这种做法会算重 i∈[1,j) 这部分的 f(Pi) 的贡献,考虑到这段 Pi 只有 n 种,线性筛预处理,暴力减。
然后就得到了:
g(n,j)={g(n,j−1)g(n,j−1)−f(Pj)(g(Pjn,j−1)−∑i=1j−1f(Pi))Pj2>notherwise
g(i,0) 的值显然,然后就可以枚举 j 递推了。设 h(n,j) 表示最小质因子大于等于 Pj 的所有 f 的和,考虑一些合数,可以枚举其最小质因子和次数:
h(n,j)=g(n,∣P∣)−i=1∑j−1f(Pi)+k≥j∑t≥1∑(f(Pkt+1)+f(Pkt)h(Pktn,k+1))
最后只需要求出 h(n,1) 即可。注意细节:前面都没有考虑 1,最后在答案里加上。
简单数论反演
欧拉反演
欧拉反演基于这样一个式子:
φ∗1=id
也就是 ∑d∣nφ(d)=n。
欧拉反演和莫比乌斯反演其实差不多,实际上,欧拉反演是莫比乌斯反演的基础。
求:
i=1∑nj=1∑nijgcd(i,j)
n≤1010。
题解
枚举 gcd(i,j) 的约数进行欧拉反演:
i=1∑j=1∑ijd∣i,j∑φ(d)
交换 ∑:
d=1∑nd2φ(d)⎝⎜⎜⎛i=1∑⌊dn⌋i⎠⎟⎟⎞2
然后数论分块解决这个问题,后面这个根据小学奥数得到 [2n(n+1)]2。前面这个东西,考虑杜教筛,令 g=id2,有:
(f∗g)(n)=d∣n∑φ(d)d2(dn)2
化简有:
(f∗g)(n)=n2d∣n∑φ(d)=n3
n3 前缀和为 6n(n+1)(2n+1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include<bits/stdc++.h> #define int long long using namespace std; const int N=10000000;int mod; inline int read(){ int x=0;bool w=0;char c=getchar(); while(c<'0' || c>'9') w|=c=='-',c=getchar(); while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return w?-x:x; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10^48); } inline int mi(int a,int p=mod-2){ int res=1; while(p){ if(p&1) res*=a,res%=mod; a*=a,a%=mod,p>>=1; } return res; }
int n,inv2,inv6;
bool isp[N+1]; int pr[N+1],cnt,p[N+1];
inline void init(int n){ memset(isp,1,sizeof(isp)),isp[1]=0; p[1]=1; for(register int i=2;i<=n;++i){ if(isp[i]){ pr[++cnt]=i; p[i]=i-1; } for(register int j=1;j<=cnt && pr[j]*i<=n;++j){ isp[i*pr[j]]=0; if(i%pr[j]==0){ p[i*pr[j]]=p[i]*pr[j]; break; } p[i*pr[j]]=p[i]*p[pr[j]]; } } for(register int i=1;i<=n;++i) p[i]=(p[i-1]+p[i]*i%mod*i%mod)%mod; }
map<int,int> mp;
inline int S2(int n){ n%=mod; return n*(n+1)%mod*(2*n+1)%mod*inv6%mod; } inline int S3(int n){ n%=mod; return mi(n*(n+1)%mod*inv2%mod,2); }
inline int Phi(int n){ if(n<=N) return p[n]; if(mp.count(n)) return mp[n]; int res=S3(n); for(register int l=2,r;l<=n;l=r+1){ r=n/(n/l); res-=(S2(r)-S2(l-1)+mod)%mod*Phi(n/l)%mod, res=(res+mod)%mod; } return mp[n]=(res+mod)%mod; }
int ans;
signed main(){ mod=read(),n=read(); inv2=mi(2),inv6=mi(6),init(N); for(register int l=1,r;l<=n;l=r+1){ r=n/(n/l); ans+=S3(n/l)*(Phi(r)-Phi(l-1)+mod)%mod, ans=(ans%mod+mod)%mod; } write(ans); return 0; }
|
莫比乌斯反演
记 F,f 满足 F(n)=∑d∣nf(d),也就是 F=f∗1,两边同时乘上一个 μ 得到 F∗μ=1∗μ∗f=ε∗f=f。
因此得到了莫比乌斯反演:
F(n)=d∣n∑f(d)⇒f(n)=d∣n∑F(d)μ(dn)
当然,更加常用的例子其实是下面两个:
- μ∗id=φ。
- μ∗1=ε。
不难发现这两个其实是莫比乌斯反演的特例。
给定 k,多组询问,每次给定 n 求:
i=1∑j=1∑(i+j)kgcd(i,j)μ2(gcd(i,j))
T≤104,k<231,n≤107。
题解
枚举约数除掉是莫反的经典操作:
d=1∑ni=1∑⌊dn⌋j=1∑⌊dn⌋d(di+dj)kμ2(d)[gcd(i,j)=1]
然后交换 ∑:
d=1∑nμ2(d)dk+1i=1∑⌊dn⌋j=1∑⌊dn⌋(i+j)k[gcd(i,j)=1]
然后 μ∗1=ε:
d=1∑ndk+1μ2(d)p=1∑⌊dn⌋μ(p)i=1∑⌊dpn⌋j=1∑⌊dpn⌋(pi+pj)k
换元,设 t=dp:
t=1∑nd∣t∑dk+1μ2(d)(dt)kμ(dt)i=1∑⌊tn⌋j=1∑⌊tn⌋(i+j)k
记 S(n)=∑i=1n∑j=1n(i+j)k,显然 tk 是完全积性函数可以筛,但是如何求 S(x) 呢?设 F(x)=∑i=1xik,G(x)=∑i=1xF(i)。
S(x)=G(2x)−2G(x)
证明
考虑数学归纳法,x=1 时显然成立。当 S(n)=G(2n)−2G(n) 时:
S(n+1)=S(n)+2i=1∑n(i+n+1)k−(2n+2)k=G(2n)−2G(n)+F(2n+1)−2F(n+1)+F(2n+2)=i=1∑2n+2F(i)−2i=1∑n+1F(i)=G(2n+2)−2G(n+1)
然后考虑剩下的部分,设 f(t)=∑d∣tdμ2(d)μ(dt)。因为它是积性函数狄利克雷卷积卷出来的,因此它也是一个积性函数。不难发现:
- f(1)=1,f(p)=p−1。
- f(p2)=−p。
- f(pk)=0(k>2),因为 d 和 dt 至少有一个能被 p2 整除。
然后就可以线性筛了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| #include<bits/stdc++.h> #define int unsigned int using namespace std; const int max_n=20000007; inline int read(){ int x=0;char c=getchar(); while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10^48); } inline int mi(int a,int p){ int res=1; while(p){ if(p&1) res*=a; a*=a,p>>=1; } return res; }
int n,k,f[max_n],s[max_n],ans;
bool isp[max_n]; int pr[max_n/100],cnt,t[max_n];
inline void init(int n){ f[1]=t[1]=1; memset(isp,1,sizeof(isp)),isp[1]=0; for(register int i=2;i<=n;++i){ if(isp[i]){ pr[++cnt]=i, f[i]=i-1,t[i]=mi(i,k); } for(register int j=1;j<=cnt && i*pr[j]<=n;++j){ isp[i*pr[j]]=0, t[i*pr[j]]=t[i]*t[pr[j]]; if(i%pr[j]==0){ if((i/pr[j])%pr[j]>0) f[i*pr[j]]=-pr[j]*f[i/pr[j]]; break; } f[i*pr[j]]=f[i]*(pr[j]-1); } } for(register int i=1;i<=n;++i) s[i]=s[i-1]+f[i]*t[i]; for(register int i=1;i<=n;++i) f[i]=f[i-1]+t[i]; for(register int i=1;i<=n;++i) f[i]=f[i-1]+f[i]; }
signed main(){ int T=read();n=read(),k=read(); init(n*2); while(T--){ n=read(),ans=0; for(register int l=1,r;l<=n;l=r+1){ r=n/(n/l); ans+=(s[r]-s[l-1])*(f[n/l*2]-f[n/l]*2); } write(ans),putchar('\n'); } return 0; }
|