这里放了一些我觉得在学习各种筛和数论定理之前,应该了解的小知识,可以对数论有一个初步的印象。包括了:
- 质数的概念,约数的概念,以及一个非常重要又简单的定理:整数唯一分解。我们应该会在后面见到它很多次。
- 解题的时候,或者写暴力的时候,我们也经常的需要求约数的各种性质,因此这里列的代码还是蛮实用的。
质数
- 定义:大于1,且除了1和它本身之外不再有其他因数的自然数。
- 质数分布定理: 1 1 1~ n n n间的质数大概有 π ( n ) = n ln n \pi(n)=\frac{n}{\ln n} π(n)=lnnn个
互质数:两个或多个整数的公因数只有1的非0自然数。
- 小性质:最小的9个质数相乘,答案在int范围内。
约数
1. 试除法求约数
(1) 求所有的质因数
void div(int x){
for (int i=2;(ll)i*i<=x;++i){
int cnt = 0;
while (x%i==0){
x /= i; cnt++;
}
if (cnt) printf("%d %d\n", i, cnt);
}
if (x > 1) printf("%d %d", x, 1);
puts("");
}
(2) 求所有的约数
使用筛法先筛质数,再通过dfs枚举约数
int n, cnt, tot, dn;
const int N = 5e4 + 5;
bool st[N];
int p[N], d[N];
pii f[N];
void dfs(int u, int x){
if (u > tot){
d[++dn] = x; return ;
}
for (int i=0;i<=f[u].yy;++i){
dfs(u + 1, x);
x *= f[u].xx;
}
}
void solve(){
int a0, a1, b0, b1;
scanf("%d%d%d%d", &a0, &a1, &b0, &b1);
int n = b1;
tot = 0;
for (int i=1;p[i]<=n/p[i];++i){
if (n % p[i] != 0) continue;
int sm = 0;
while (n % p[i] == 0) n /= p[i], ++sm;
f[++tot] = pii(p[i], sm);
}
if (n > 1) f[++tot] = pii(n, 1);
dn = 0;
dfs(1, 1);
}
2. 约数个数
任何一个正整数 n n n都可以表达为如下形式:
n = p 1 a 1 × p 2 a 2 × . . . × p k a k n=p_1^{a_1}\times p_2^{a_2}\times ...\times p_k^{a_k} n=p1a1×p2a2×...×pkak,其中, ∀ i ∈ [ 1 , k ] , p i \forall i\in [1,k], p_i ∀i∈[1,k],pi为质数
那么,对于 n n n的任意约数 α \alpha α,都可以将 α \alpha α写成如下形式:
α = p 1 b 1 × p 2 b 2 × . . . × p k b k \alpha=p_1^{b_1}\times p_2^{b_2}\times ...\times p_k^{b_k} α=p1b1×p2b2×...×pkbk,其中, ∀ i ∈ [ 1 , k ] \forall i\in [1,k] ∀i∈[1,k], 0 ≤ b i ≤ a k 0\leq b_i \leq a_k 0≤bi≤ak
那么对于每一个 b i b_i bi,都有 ( a i + 1 ) (a_i+1) (ai+1)种取法,因此, n n n的所有约数之和为 ( a 1 + 1 ) ( a 2 + 1 ) . . . ( a k + 1 ) (a_1+1)(a_2+1)...(a_k+1) (a1+1)(a2+1)...(ak+1)
- 小性质:所有int范围内的数,约数之和最多的约为1500
小例子:求n!的约数之和
int n, cnt;
const int N = 1e6 + 5;
int p[N], num[N];
bool st[N];
void Euler(int n){
for (int i=2;i<=n;++i){
if (!st[i]) p[++cnt] = i;
for (int j=1;p[j]<=n/i;++j){
st[p[j] * i] = true;
if (i % p[j] == 0) break;
}
}
}
int main(void){
scanf("%d", &n);
Euler(N - 1);
for (int i=2;i<=n;++i){
if (st[i]) continue;
for (ll j = i;j<=n;j*=i){
num[i] += n / j;
}
}
for (int i=2;i<=n;++i){
if (num[i]){
printf("%d %d\n", i, num[i]);
}
}
return 0;
}
3. 约数之和
∑ α i = ( p 1 0 + p 1 1 + . . . + p 1 a 1 ) ( p 2 0 + p 2 1 + . . . + p 2 a 2 ) . . . ( p k 0 + p k 1 + . . . + p k a k ) \sum \alpha_i=(p_1^0+ p_1^1+ ...+ p_1^{a_1})(p_2^0+ p_2^1+ ...+ p_2^{a_2})...(p_k^0+ p_k^1+ ...+ p_k^{a_k}) ∑αi=(p10+p11+...+p1a1)(p20+p21+...+p2a2)...(pk0+pk1+...+pkak)
展开该式子,就可以得到 α = p 1 b 1 × p 2 b 2 × . . . × p k b k \alpha=p_1^{b_1}\times p_2^{b_2}\times ...\times p_k^{b_k} α=p1b1×p2b2×...×pkbk,所有的 b i b_i bi取值的组合相加。实际上,这个式子展开一共有 ( a 1 + 1 ) ( a 2 + 1 ) . . ( a k + 1 ) (a_1+1)(a_2+1)..(a_k+1) (a1+1)(a2+1)..(ak+1)项,也对应了约数个数。