引言
在数论问题中,积性函数有着广泛的应用。
如在莫比乌斯反演问题中,函数变换之后如何快速维护前缀和往往是最重要也是最难的一步。如果维护的函数具有积性,那就可以尝试利用线性筛在
本文的大部分相关性质及公式来自:
博主将试着证明其中的性质公式,严谨性可能欠缺,其目的主要是帮助记忆和理解。
因水平有限 若有错误之处还望指出qwq~
积性函数的定义和性质
定义: 对于一个定义域为
若对于任意整数
性质:
(1)对于任意积性函数
证明:因
因
证毕。
(2)对于一个大于
若
证明:由积性和完全积性的定义易得。
欧拉函数
φ
定义:对于正整数
定义式:若
性质:
(1)欧拉函数为积性函数,而不是完全积性函数。
证明:设两个互质的正整数
则:
因
故推出:
积性函数性质得证。
而完全积性由上证明可见,
(2)假设存在一个素数
证明:
可以从反面来思考这件事,在小于
故由定义:
证毕。
另外还可以从定义式出发去理解这个结论。
(3)欧拉定理:若有互质的两个正整数
此定理常用来求解逆元。
证明:(可参考百度百科)
(4)假设存在一个正整数n,则:
证明:
先考虑特殊情况:
对于一般情况,
当n只含一个质因子时,即
有:
显然,将求和式展开,会有一些项可以合并。
比如
可见:
当
利用积性函数的性质有
同上易得:
证毕。
(5)当
证明:在数论求
由第一个公式,知:
若
这样就能将保证当
故
当
证毕。
例题:HDU 3501
题意:求小于n且与n不互质的数之和。
思路:直接利用公式求反面,总数 - 反面数即为答案。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
const int A = 1e5 + 10;
bool vis[A];
int pri[A],tot;
void init(){
tot = 0;
vis[0] = vis[1] = 1;
for(int i=2 ;i<A ;i++){
if(vis[i] == 0) pri[++tot] = i;
for(int j=1 ;j<=tot&&pri[j]*i<A ;j++){
vis[pri[j]*i] = 1;
if(i%pri[j] == 0) break;
}
}
}
int main(){
init();
ll n;
while(~scanf("%I64d",&n) && n){
ll ans = (n*(n-1)/2)%mod;
ll res = n,m = n;
for(int i=1 ;i<=tot&&pri[i]*pri[i]<=n ;i++){
if(n%pri[i] == 0){
res = res/pri[i]*(pri[i]-1);
while(n%pri[i] == 0) n/=pri[i];
}
}
if(n!=1) res = res/n*(n-1);
ans = ans - res*m/2;
printf("%I64d\n",(ans%mod+mod)%mod);
}
return 0;
}
莫比乌斯函数
μ
定义式:
性质:
(1)对于任意正整数n,有:
该性质在莫比乌斯反演中有着极大应用。
证明:
当
当
考虑莫比乌斯函数取值的特殊性,若函数值不为
故
由二项式定理,原式等于:
综上所述:
当
当
证毕。
(2)对于任意正整数
证明:
令
由欧拉函数的性质
又由莫比乌斯反演:
推出:
等式两边同除以n,得:
证毕。
线性筛求积性函数
欧拉函数
φ
考虑将所有数分成三类:
(1)质数
(2)最小质因子指数为1:
(3)最小质因子指数大于1:
代码:
void init(){
tot = 0;phi[1] = 1;
for(int i=2 ;i<A ;i++){
if(!vis[i]){pri[++tot] = i;phi[i] = i-1;}
for(int j=1 ;j<=tot&&i*pri[j]<A ;j++){
vis[i*pri[j]] = 1;
if(i%pri[j] == 0){
phi[i*pri[j]] = pri[j] * phi[i];break;
}
phi[i*pri[j]] = phi[pri[j]] * phi[i];
}
}
}
莫比乌斯函数
μ
因为线性筛每次都是用最小质因子去筛每一个数。
故也可分为三类:
(1)
(2)
(3)
代码:
void init(){
tot = 0;mu[1] = 1;
for(int i=2 ;i<A ;i++){
if(!vis[i]){pri[++tot] = i;mu[i] = -1;}
for(int j=1 ;j<=tot&&i*pri[j]<A ;j++){
vis[i*pri[j]] = 1;
if(i%pri[j] == 0){mu[i*pri[j]] = 0;break;}
mu[i*pri[j]] = -mu[i];
}
}
}
以下为其他常见积性函数的线性筛:
约数个数函数d
仍然分三类讨论,此时我们还需要维护每一个数
(1)
(2)
(3)
代码:
void init(){
tot = 0;d[1] = 1;
for(int i=2 ;i<A ;i++){
if(!vis[i]){pri[++tot] = i;d[i] = 2;cnt[i] = 1;}
for(int j=1 ;j<=tot&&i*pri[j]<A ;j++){
vis[i*pri[j]] = 1;
if(i%pri[j] == 0){
d[i*pri[j]] = d[i]/(cnt[i]+1)*(cnt[i]+2);
cnt[i*pri[j]] = cnt[i] + 1;
break;
}
d[i*pri[j]] = d[i]<<1;
cnt[i*pri[j]] = 1;
}
}
}
约数和函数
σ
假设一个正整数
则
假设最小质因子为
故我们可以维护两个量:
然后又可以把所有数分成三类:
(1)
(2)
(3)
代码:
void init(){
tot = 0;Sigma[1] = 1;
for(int i=2 ;i<A ;i++){
if(!vis[i]){
pri[++tot] = i;
Sigma[i] = 1 + i;
sum[i] = 1 + i;
Mx[i] = i;
}
for(int j=1 ;j<=tot&&i*pri[j]<A ;j++){
vis[i*pri[j]] = 1;
if(i%pri[j] == 0){
Sigma[i*pri[j]] = Sigma[i]/sum[i]*(sum[i] + Mx[i]*pri[j]);
sum[i*pri[j]] = sum[i] + Mx[i]*pri[j];
Mx[i*pri[j]] = Mx[i]*pri[j];
break;
}
Sigma[i*pri[j]] = Sigma[i]*(pri[j] + 1);
sum[i*pri[j]] = pri[j] + 1;
Mx[i*pri[j]] = pri[j];
}
}
}