一些性质
- 积性函数:对于函数 f(n) ,若满足对任意互质的数字 a,b,a∗b=n 且 f(n)=f(a)f(b) ,那么称函数f为积性函数。
- 狄利克雷卷积:对于函数f,g,定义它们的卷积为
(f∗g)(n)=∑d|nf(d)g(nd) 。
狄利克雷卷积满足很多性质:
交换律: f∗g=g∗f
结合律: (f∗g)∗h=f∗(g∗h) - 两个积性函数的狄利克雷卷积仍为积性函数。
积性函数都可以用线性筛筛出来
怎么筛?
一般来讲,只要知道f(Pk),P为质数,就可以知道怎么筛了
简单来讲就是推一下
通俗来讲就是手玩一下或者打表找规律
如果你一眼就看出来了那就只能Orz了几道题
一些常见积性函数的筛法
莫比乌斯函数 μ(n)
这个比较简单,
μ(1)=1
,
i
为质数时
isprime[1] = 1; mu[1] = 1;
for(int i = 2; i < N; ++i){
if(!isprime[i]){ prime[++num] = i; mu[i] = -1; }
for(int j = 1; j <= num && i * prime[j] < N; ++j){
isprime[i * prime[j]] = 1;
if(i % prime[j]) mu[i * prime[j]] = -mu[i];
else{ mu[i * prime[j]] = 0; break; }
}
}
乘法逆元inv(i)
求一个数在模p意义下的逆元
设
p=i∗x+j
,则
i∗x+j≡0(mod p)
同时除以
i∗j,所以x∗j−1+i−1≡0(mod p)
移项
i−1≡−x∗j−1(mod p)
而
x=p div i,j=p mod i
所以
inv(i)=−inv(p%i)∗(p/i)
不用筛了,递推就可以了
inv[1] = 1; for(int i = 2; i < p; ++i) inv[i] = -(p / i) * inv[p % i] % p + p) % p;
补充阶乘逆元的递推,求出 inv(n) , inv(i)=inv(i+1)∗(i+1) 倒着来就行了
fac[0] = inv[0] = 1;
for(int i = 1; i <= n; ++i) fac[i] = fac[i] * i % p;
inv[n] = Getinv(fac[n]); //Exgcd or Fermat
for(int i = 1; i < n; i++) inv[i] = inv[i + 1] * (i + 1) % p;
欧拉函数 φ(n)
公式:
n分解成若干质数p的乘积n=Πpaii
φ(n)=n∗Π(1−1pi)
那么 φ(1)=1 , n为质数时φ(n)=n−1 ,最小质因子筛到它时乘上质因子 p−1 ,否则乘上这个质数
isprime[1] = 1; phi[1] = 1;
for(int i = 2; i < N; ++i){
if(!isprime[i]){ prime[++num] = i; phi[i] = i - 1; }
for(int j = 1; j <= num && i * prime[j] < N; ++j){
isprime[i * prime[j]] = 1;
if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
else{ phi[i * prime[j]] = phi[i] * prime[j]; break; }
}
}
约数个数 d(n)
公式:
还是分解
d(n)=Π(ai+1)
我们记录下每个数最小质因子的指数记为 pred 就好了
isprime[1] = 1; d[1] = 1;
for(int i = 2; i < N; ++i){
if(!isprime[i]){ prime[++num] = i; d[i] = 2; pred[i] = 1; }
for(int j = 1; j <= num && i * prime[j] < N; ++j){
isprime[i * prime[j]] = 1;
if(i % prime[j]) d[i * prime[j]] = d[i] * d[prime[j]], pred[i * prime[j]] = 1;
else{ pred[i * prime[j]] = pred[i] + 1; d[i * prime[j]] = d[i] / (pred[i] + 1) * (pred[i] + 2); break; }
}
}
约数的和 σ(n)
公式:
又是分解
σ(n)=Π(∑aij=0pji)
这个就很烦了。。。
也可以筛,开两个个数组,一个 powd 记录每个数最小质因子的指数次幂,另一个 sumd 记录每个数最小质因子 ∑ai=0pi 就可以了
我们把这个鬼里鬼气的 σ写成f
IL void Prepare(){
isprime[1] = 1; f[1] = mu[1] = 1;
for(int i = 2; i < N; ++i){
if(!isprime[i]){
prime[++num] = i; f[i] = i + 1; mu[i] = -1;
sumd[i] = 1 + i; powd[i] = i;
}
for(int j = 1; j <= num && i * prime[j] < N; ++j){
isprime[i * prime[j]] = 1;
if(i % prime[j]){
sumd[i * prime[j]] = 1 + prime[j]; powd[i * prime[j]] = prime[j];
f[i * prime[j]] = f[i] * f[prime[j]];
}
else{
powd[i * prime[j]] = powd[i] * prime[j];
sumd[i * prime[j]] = sumd[i] + powd[i * prime[j]];
f[i * prime[j]] = f[i] / sumd[i] * sumd[i * prime[j]];
break;
}
}
}
}