这是我在ACM竞赛中学习数论时整理的一些基础的知识点,这篇博客主要讨论数论中出现的一些数论函数和相关的一些算法。如果在理解上有所困难,请看数论基础知识(基础篇)
又称整数的唯一分解定理。
算术基本定理是初等数论中一个基本的定理,也是许多其他定理的逻辑支撑点和出发点。
正整数a, b,根据算术基本定理,a,b可以分别表示为:
a = p 1 α a 1 p 2 α a 2 . . . p m α a m a=p_{1}^{\alpha_{a_{1}}}p_{2}^{\alpha_{a_{2}}}...p_{m}^{\alpha_{a_{m}}} a=p1αa1p2αa2...pmαam
b = p 1 α b 1 p 2 α b 2 . . . p m α b m b=p_{1}^{\alpha_{b_{1}}}p_{2}^{\alpha_{b_{2}}}...p_{m}^{\alpha_{b_{m}}} b=p1αb1p2αb2...pmαbm
g c d ( a , b ) gcd(a,b) gcd(a,b)的本质就是:
g c d ( a , b ) = p 1 m i n { α a 1 , α b 1 } p 2 m i n { α a 2 , α b 2 } . . . p m m i n { α a m , α b m } gcd(a,b)=p_{1}^{min\{\alpha_{a_{1}},\alpha_{b_{1}}\}}p_2^{min\{\alpha_{a_{2}},\alpha_{b_{2}}\}}...p_{m}^{min\{\alpha_{a_{m}},\alpha_{b_{m}}\}} gcd(a,b)=p1min{αa1,αb1}p2min{αa2,αb2}...pmmin{αam,αbm}
l c m ( a , b ) lcm(a,b) lcm(a,b)的本质就是:
l c m ( a , b ) = p 1 m a x { α a 1 , α b 1 } p 2 m a x { α a 2 , α b 2 } . . . p m m a x { α a m , α b m } lcm(a,b)=p_{1}^{max\{\alpha_{a_{1}},\alpha_{b_{1}}\}}p_2^{max\{\alpha_{a_{2}},\alpha_{b_{2}}\}}...p_{m}^{max\{\alpha_{a_{m}},\alpha_{b_{m}}\}} lcm(a,b)=p1max{αa1,αb1}p2max{αa2,αb2}...pmmax{αam,αbm}
因此 a b = l c m ( a , b ) ∗ g c d ( a , b ) ab=lcm(a,b)*gcd(a,b) ab=lcm(a,b)∗gcd(a,b)
假设正整数表示为: n = p 1 α 1 p 2 α 2 . . . p m α m n=p_{1}^{\alpha_{1}}p_{2}^{\alpha_{2}}...p_{m}^{\alpha_{m}} n=p1α1p2α2...pmαm
定义: 1~n 中与 n 互质的数的个数被称为欧拉函数,记为 φ ( N ) \varphi(N) φ(N)。
计算公式: φ ( n ) = n ∗ p 1 − 1 p 1 ∗ p 2 − 1 p 2 ∗ . . . ∗ p m − 1 p m \varphi(n)=n*\frac{p_{1}-1}{p_{1}}*\frac{p_{2}-1}{p_{2}}*...*\frac{p_{m}-1}{p_{m}} φ(n)=n∗p1p1−1∗p2p2−1∗...∗pmpm−1
性质:
1. 任意n>1,1~n中与n互质的数的和为 n ∗ φ ( n ) 2 \frac{n*\varphi(n)}{2} 2n∗φ(n)
2. ∑ d ∣ n φ ( d ) = n \sum_{d\mid n}\varphi(d)=n ∑d∣nφ(d)=n
3. 当n为质数时, φ ( n ) = n − 1 \varphi(n)=n-1 φ(n)=n−1
求解方法:
int eluer(int n)
{
int ans = n;
for(int i = 2; i * i <= n; i ++){
if(n % i == 0) {
ans = (ans / i) * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = (ans / n) * (n - 1);
return ans;
}
int maxn = 1e6 + 100;
bool v[maxn];
int phi[maxn], p[maxn];
void sieze(int n)
{
int m = 0; memset(v, false, sizeof(v));
for(int i = 2; i <= n; i ++){
if(!v[i]){
p[++ m] = i; phi[i] = i-1;
}
for(int j = 1; j <= m && i * p[j] <= n; j ++){
v[i * p[j]] = true;
if(i % p[j]) phi[i * p[j]] = phi[i] * p[j];
else {
phi[i * p[j]] = phi[i] * phi[p[j]];
break;
}
}
}
}
莫比乌斯函数
通俗的说,对于n如果存在平方因子,则 μ ( n ) = 0 \mu(n)=0 μ(n)=0,若不存在平方因子,n的质因数的个数为m,则 μ ( n ) = ( − 1 ) m \mu(n)=(-1)^{m} μ(n)=(−1)m。
int mu(int n)
{
int m = 0;
for(int i = 2; i * i <= n; i ++){
if(n % i == 0){
int cnt = 0;
m ++;
while(n % i == 0){
cnt ++;
n /= i;
if(cnt >= 2) return 0;
}
}
}
if(n > 1) m ++;
return m % 2 == 0 ? 1 : -1;
}
const int maxn = 1e5 + 10;
bool v[maxn];
int mu[maxn], p[maxn];
void sieze(int n)
{
int m = 0; memset(v, false, sizeof(v));
mu[1] = 1;
for(int i = 2; i <= n; i ++){
if(!v[i]){
p[++ m] = i; mu[i] = -1;
}
for(int j = 1; j <= m && i * p[j] <= n; j ++){
v[i * p[j]] = true;
if(i % p[j]) {
mu[i * p[j]] = -mu[i];
}
else {
mu[i * p[j]] = 0;
break;
}
}
}
}
int d(n)
{
int ans = 1;
for(int i = 2; i * i <= n; i ++){
if(n % i == 0){
int cnt = 0;
while(n % i == 0){
n /= i;
cnt ++;
}
ans *= (cnt + 1);
}
}
if(n > 1) ans *= 2;
return ans;
}
const int maxn = 1e5 + 100;
int p[maxn], num[maxn], d[maxn];
bool v[maxn];
int m = 0;
void sieze(int n)
{
m = 0; memset(v, false, sizeof(v));
d[1] = 1; num[1] = 1;
for(int i = 2; i <= n; i ++){
if(!v[i]){
p[++ m] = i; d[i] = 2; num[i] = 1;
}
for(int j = 1; j <= m && i * p[j] <= n; j ++){
v[i * p[j]] = true;
if(i % p[j]){
d[i * p[j]] = d[i] * d[p[j]];
num[i * p[j]] = 1;
}
else {
d[i * p[j]] = (d[i] / (num[i] + 1)) * (num[i] + 2);
num[i * p[j]] = num[i] + 1;
break;
}
}
}
}
定义: 两个数论函数f(n)和g(n)作巻积记为 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) ∗ g ( n d ) (f*g)(n)=\sum_{d\mid n}f(d)*g(\frac{n}{d}) (f∗g)(n)=∑d∣nf(d)∗g(dn),简记为 f ∗ g f*g f∗g
运算性质: 以下运算性质都可根据定义得到
几个重要的巻积
注:两个积性函数作巻积,结果仍然为积性函数
之前的欧拉函数、莫比乌斯函数、约数个数函数 O ( n ) O(n) O(n)计算1~n以内的所有的函数,就是积性函数线性筛的做法,原理基本相同。只要是积性函数,原则上都能线性筛出1~n以内的所有函数值。
以约数和个数为例,令 f ( n ) = σ ( n ) f(n)=\sigma(n) f(n)=σ(n)
当n是质数时, f ( n ) = n + 1 f(n)=n+1 f(n)=n+1
将正整数n分解成 n = p 1 α 1 p 2 α 2 . . . p m α m , 满 足 p 1 < p 2 < . . . < p m n=p_{1}^{\alpha_{1}}p_{2}^{\alpha_{2}}...p_{m}^{\alpha_{m}},满足p_{1}
定义 l o w [ n ] = p 1 α 1 low[n]=p_{1}^{\alpha_{1}} low[n]=p1α1
当 p ∣ n 且 p 2 ∤ n p\mid n且p^{2}\nmid n p∣n且p2∤n,则 p p p与 n p \frac{n}{p} pn互质,则 d ( n ) = d ( n d ) ∗ d ( p ) d(n)=d(\frac{n}{d})*d(p) d(n)=d(dn)∗d(p)
当 p ∣ n 且 p 2 ∣ n p\mid n且p^{2}\mid n p∣n且p2∣n,若 l o w ( n p ) = n p , f ( n ) = f ( n p ) ∗ p + 1 low(\frac{n}{p})=\frac{n}{p},f(n)=f(\frac{n}{p})*p+1 low(pn)=pn,f(n)=f(pn)∗p+1,否则, n p l o w [ n p ] \frac{\frac{n}{p}}{low[\frac{n}{p}]} low[pn]pn与 l o w [ n p ] ∗ p low[\frac{n}{p}]*p low[pn]∗p互质,则 f ( n ) = f ( n p l o w [ n p ] ) ∗ f ( l o w [ n p ] ∗ p ) f(n)=f(\frac{\frac{n}{p}}{low[\frac{n}{p}]})*f(low[\frac{n}{p}]*p) f(n)=f(low[pn]pn)∗f(low[pn]∗p)
代码实现:
const int maxn = 1e5 + 100;
int p[maxn], low[maxn], f[maxn];
bool v[maxn];
int m = 0;
void sieze(int n)
{
m = 0; memset(v, false, sizeof(v));
f[1] = 1; low[1] = 1;
for(int i = 2; i <= n; i ++){
if(!v[i]){
p[++ m] = i; f[i] = i + 1; low[i] = i;
}
for(int j = 1; j <= m && i * p[j] <= n; j ++){
v[i * p[j]] = true;
if(i % p[j]){
f[i * p[j]] = f[i] * f[p[j]];
low[i * p[j]] = p[j];
}
else {
if(low[i] == i) f[i * p[j]] = f[i] * p[j] + 1;
else f[i * p[j]] = f[i / low[i]] * f[low[i] * p[j]];
low[i * p[j]] = low[i] * p[j];
break;
}
}
}
}