21.3.12 数论总结(一) 质数&因数

质数

欧拉筛 O ( n ) O(n) O(n)

int prime[N],tot = 0;
bool notprime[N];
void get_prime() {
	for(int i = 2; i <= N ;i++) {
		if(!notprime[i]) prime[++tot] = i;
		for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
			notprime[i * prime[j]] = true;
			if(i % prime[j] == 0) break;
		}
	}
}
证明

在埃氏筛中每个合数会被其每个质因子筛一遍,考虑能否只被其最小的质因子筛,这样复杂度就能变为 O ( n ) O(n) O(n)
只用证明两件事:

  1. 每一个合数都会被筛到。
  2. 每一个合数都仅会被其最小质因子筛到

设被筛去的合数 c = i ∗ p r i m e [ j ] ( c < = N ) c = i * prime[j] (c <= N) c=iprime[j](c<=N)

  • 对于 1 1 1,要证会被 i ∗ p r i m e [ j ] i * prime[j] iprime[j]筛到,即证在代码中不会提前终止循环。
    p r i m e [ j ] prime[j] prime[j] c c c 的最小质因子时,首先显然 p r i m e [ j ] < i prime[j]<i prime[j]<i。由于 p r i m e [ j ] prime[j] prime[j] c c c 的最小质因子,故 i i i 中没有比 p r i m e [ j ] prime[j] prime[j] 更小的质因子故不会出现 i   m o d   p r i m e [ k ] = = 0 ( k < j ) i \bmod prime[k] == 0(k < j) imodprime[k]==0(k<j)
  • 对于 2 2 2,假设会被非最小质因子 p r i m e [ j ] prime[j] prime[j] 筛去,此时 i i i 中含有比 p r i m e [ j ] prime[j] prime[j] 更小的质因子,显然会在某个 k ( k < j ) k(k < j) k(k<j) 时满足 i   m o d   p r i m e [ k ] = = 0 i \bmod prime[k] == 0 imodprime[k]==0 而终止循环。

质因数分解

算数基本定理

任何一个大于 1 1 1 的正整数都能唯一分解为有限个质数的乘积,可写作:
N = p 1 c 1 ∗ p 2 c 2 ∗ … ∗ p m c m ( p 1 < p 2 < … < p m ) N = p_1^{c_1} * p_2^{c_2} *…*p_m^{c_m}(p_1 < p_2 < … <p_m) N=p1c1p2c2pmcm(p1<p2<<pm)

试除法(分解单个数) O ( n ) O(\sqrt{n}) O(n )
int m = 0;
void divide(int n) {
	for(int i = 2; i <= sqrt(n); i++) {
		if(n % i == 0) {
			prime[++m] = i,c[m] = 0;
			while(n % i == 0) n /= i,c[m]++;
		}
	}
	if(n > 1) prime[++m] = n,c[m] = 1;
}
推荐练习

阶乘分解

题解

分别把 1 − N 1 - N 1N每个数分解质因数 O ( N N ) O(N\sqrt{N}) O(NN )显然会超时,所以我们可以先筛出 1 − N 1 - N 1N中的质数,然后对于每个质数 p i p_i pi,求出 N ! N! N! 中共包含了几个质因子 p i p_i pi,在 1 − N 1 - N 1N p i p_i pi 的倍数即至少包含一个质因子 p i p_i pi 的数显然有 ⌊ N p i ⌋ \left \lfloor \frac{N}{p_i} \right \rfloor piN个,在 1 − N 1 - N 1N p i 2 p_i^{2} pi2 的倍数即至少包含一个质因子 p i p_i pi 的数显然有 ⌊ N p i 2 ⌋ \left \lfloor \frac{N}{p_i^{2}} \right \rfloor pi2N个,不过因为其中的一个质因子在 ⌊ N p i ⌋ \left \lfloor \frac{N}{p_i} \right \rfloor piN中已经统计了,所以只需要再统计第 2 2 2个质因子,依次类推。

原因

可以类比以下代码

for(int i = p; i <= n; i += p)

其中的每一个 i i i 都至少包含一个质因子 p p p,这样的数显然有 ⌊ N p i ⌋ \left \lfloor \frac{N}{p_i} \right \rfloor piN个。
综上, N ! N! N! 中质因子 p i p_i pi 的个数为:
⌊ N p i ⌋ + ⌊ N p i 2 ⌋ + … + ⌊ N p i ⌊ log ⁡ p N ⌋ ⌋ = ∑ p i k ≤ N ⌊ N p i k ⌋ \left \lfloor \frac{N}{p_i} \right \rfloor +\left \lfloor \frac{N}{p_i^{2}} \right \rfloor + …+\left \lfloor \frac{N}{p_i^{\left \lfloor \log_p N \right \rfloor}} \right \rfloor = \sum_{p_i^{k}\leq N} \left \lfloor \frac{N}{p_i^{k}} \right \rfloor piN+pi2N++pilogpNN=pikNpikN

代码
#include<cstdio>
const int N = 1e6 + 5;
int prime[N],tot = 0,n;
bool notprime[N];
void get_prime() {
	for(int i = 2; i <= n ;i++) {
		if(!notprime[i]) prime[++tot] = i;
		for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
			notprime[i * prime[j]] = true;
			if(i % prime[j] == 0) break;
		}
	}
}
int main() {
	scanf("%d",&n);
	get_prime();
	for(int i = 1; i <= tot; i++) {
		int p = prime[i],ans = 0;
		for(long long j = p; j <= n; j *= p) ans += (n / j);
		//注意一个细节,变量j会爆int
		//可以变乘为除,代码如下
		//for(int j = n; j; j /= p) ans += (j / p);
		printf("%d %d\n",p,ans);
	}
	return 0;
}

约数

算数基本定理的推论

N = p 1 c 1 ∗ p 2 c 2 ∗ … ∗ p m c m N = p_1^{c_1} * p_2^{c_2} *…*p_m^{c_m} N=p1c1p2c2pmcm的正约数集合可以写作:
{ p 1 b 1 p 2 b 2 … p m b m } ( 0 ≤ b i ≤ c i ) \left\{ p_1^{b_1} p_2^{b_2} …p_m^{b_m} \right\}(0\leq b_i \leq c_i) {p1b1p2b2pmbm}(0bici)

N 的正约数个数为:

( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ … ∗ ( c m + 1 ) = ∏ i = 1 m ( c i + 1 ) (c_1+1) * (c_2+1) * …* (c_m+1) = \prod_{i = 1}^m (c_i+1) (c1+1)(c2+1)(cm+1)=i=1m(ci+1)

N 的所有正约数的和为:

( 1 + p 1 + p 1 2 + … + p 1 c 1 ) ∗ … ∗ ( 1 + p m + p m 2 + … + p m c m ) = ∏ i = 1 m ( ∑ j = 0 c i ( p i ) j ) (1+p_1+p_1^{2}+…+p_1^{c_1})*…*(1+p_m+p_m^{2}+…+p_m^{c_m}) = \prod_{i = 1}^{m} (\sum_{j = 0} ^{c_i} (p_i)^j) (1+p1+p12++p1c1)(1+pm+pm2++pmcm)=i=1m(j=0ci(pi)j)

求 N 的正约数集合——试除法 O ( N ) O(\sqrt{N}) O(N )

int factor[1600],m = 0;

for(int i = 1; i * i <= n; i++)
		if(n % i == 0) {
			factor[++m] = i;
			if(i != n / i) factor[++m] = n / i;
		}

注意 f a c t o r [ ] factor[] factor[]中的数不是从小到大的顺序,而是成对的。

求1 ~ N 每个数的正约数集合——倍数法 O ( N log ⁡ N ) O(N\log N) O(NlogN)

#include<cstdio>
#include<vector>
const int N = 20;
std::vector<int> factor[N];
int main() {
	for(int i = 1; i <= N; i++)
		for(int j = 1; j <= N / i; j++)
			factor[i * j].push_back(i);
	for(int i = 1; i <= N; i++) {
		for(int j = 0; j < factor[i].size(); j++)
			printf("%d ",factor[i][j]);
		puts("");
	}
	return 0;
}
推荐练习

反素数

最大公约数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值