数论基础之素数

质因子分解

唯一分解定理

任何数都可由素因子之积构成

  • N = p 1 a 1 × p 2 a 2 × p 3 a 3 … … p n a n N=p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3}……p_n^{a_n} N=p1a1×p2a2×p3a3……pnan ( p i p_i pi 为素因子)

方法1:暴力分解

枚举因子,并暴力分解,时间复杂度: O ( n 1 / 2 ) O(n^{1/2}) O(n1/2)

int p[100],a[100],cnt;
void divide(int n){
	int ans=0;
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			p[++cnt]=i;
			while(n%i==0){
				n/=i;
				a[cnt]++;
			}
		} 
	}
	if(n>1){
		p[++cnt]=n;
		a[cnt]=1;
	}
	for(int i=1;i<=cnt;i++)cout<<p[i]<<" "<<a[i]<<endl; 
}

方法2:预处理一个 nxt 数组再分解

使用埃筛或欧拉筛,预处理出 nxt 数组,其中 nxt[i] 表示 i 的最小质因子。预处理出 nxt 数组之后,对每个数质因子分解的时间复杂度为: O ( l o g ) O(log) O(log) 。总时间复杂度: O ( n + T l o g n ) O(n+Tlogn) O(n+Tlogn)

//埃筛,时间复杂度:O(nloglogn),大约2e6适用
void init() { 
	for(long long i=2; i<=N; i++) {
		if(nxt[i]!=0)continue;
		nxt[i]=i;
		for(long long j=i*i; j<=N; j+=i) {
			if(nxt[j]==0)nxt[j]=i;
		}
	}
}
//欧拉筛,时间复杂度:O(n),大约2e7适用
void init(){
}

void div_it(int x) {
	while(x!=1) {
		int add=0,div=next[x];
		while(next[x]==div) {
			add++;
			x=x/div;
		}
        a.push_back(make_pair(div,add));
	}
    //保证div是从小到大的。
}

判断质数

方法1:试除法

原理:判断 n 1 / 2 n^{1/2} n1/2 之内是否有因子即可。

int isPrime(int x){
	if(x<2)return 0;
	int t=sqrt(x);
	for(int i=2;i<=t;i++){
		if(x%i==0)return 0;
	}
	return 1;
}

方法2:埃筛

原理:对于如何一个非 1 1 1 的正整数,其大于 1 的整数倍,均不可能是质数。时间复杂度: O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

【补充】时间复杂度较欧拉筛高,但功能性强,可以处理出的信息更多。

const int N=2e6+10,R=2e6;
int prime[N],cnt;//素数表
int p[N];//判断素数 
void init(){
	p[0]=p[1]=1;
	for(int i=2;i<=N;i++){
		if(p[i]==1)continue;
		prime[++cnt]=i;
		for(int j=2*i;j<=N;j+=i)p[j]=1;
	}
}

方法3:欧拉筛

原理:与埃筛类似。唯一的优点是: 。时间复杂度: O ( n ) O(n) O(n)

const int N=2e6+10,R=2e6;
int prime[N],cnt;//素数表
int p[N];//判断素数 
void init(){
	p[0]=p[1]=1;//0则素数,1则非素数 
	for(int i=2;i<=N;i++){
		if(p[i]==0)prime[++cnt]=i;//判断 i是否为素质 
		for(int j=1;j<=cnt;j++){//每个已知素数的 i倍都会被标记
			if(i*prime[j]>N)break;//范围越界就break 
			p[i*prime[j]]=1;
			if(i%prime[j]==0)break;//保证了每个数只会被标记一次 
		}
	}
}

例题练习:

问题1:因子个数

方法1:质因子分解

a n s = ∏ ( k i + 1 ) ans=\prod (k_i+1) ans=(ki+1) 。其中 k i k_i ki 是每个质因子的指数。

O ( n + T l o g n ) O(n+Tlogn) O(n+Tlogn) 即可求出 T T T 个数的因子个数。

方法2:调和级数

对于每个数 i i i,枚举其整数数字 j j j ,显然 j j j i i i 这个因子。

O ( n l o g n ) O(nlogn) O(nlogn) 即可求出 [ 1 − n ] [1-n] [1n] 内每个数的因子个数。

该方法甚至可以求出每个数的因子集合。

for(int i=1;i<=n;i++){
    for(int j=i;j<=n;j+=i){
        f[j].push_back(i); 
    }
}

问题2:求 [1,n] 中所有数的因子个数一共有多少个

方法:整除分块

如果用调和级数求,需要 n l o g n nlogn nlogn 求。

枚举因子 i i i [ 1 , N ] [1,N] [1,N] 中一共有 [ N / i ] [N/i] [N/i] 个数有 i i i 这个因子。

因此累加就是: O ( ∑ i = 1 n [ n / i ] ) O(\sum_{i=1}^n [n/i]) O(i=1n[n/i])

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值