特别基础的数论

本文主要讲了线性筛素数、分解质因数算法。

线性筛素数

P3383 【模板】线性筛素数

这里只讲欧拉筛

算法思路:若是枚举到一个数\(x\),如果它没被标记成合数,那么加入素数数组,同时再用一个循环把所有小于x最小质因子的质数乘以x的数标记成合数。这样是有线性复杂度的。

既然是线性的正确算法,那应该能保证没漏筛、没重复筛。

证明:

设合数 \({x}\) 有两个质因子 \({a,b(a<b)}\)

\(s\) 可以表示成 \(s=a\times\dfrac{s}{a}=b\times\dfrac{s}{b}\)

可以得到 \(\dfrac{s}{b}<\dfrac{s}{a}<s\)

那么我们就可以用 \(s\) 最小的质因子来筛掉 \(s\),就只会筛一次。

不妨设 \(a\) 就是最小的质因子,那么 \(\dfrac{s}{a}\) 的最小的质因子一定大于等于 \(a\),那么欧拉筛就一定在枚举到 \(\dfrac{s}{a}\) 能筛掉 \(s\)

但是在枚举到 \(\dfrac{s}{b}\) 时,因为它的最小的质因子一定大于 \(a\),那么欧拉筛就不会筛到 \(s\)

综上所述,\(s\) 当且仅当枚举到 \(\dfrac{s}{a}\) 时会被筛,所以就不会重复筛也不会漏筛。

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e8+100;
bool isp[MAXN];
int prime[MAXN>>4],pcnt;
void init(int n) {
	for(int i=2; i<=n; ++i) {
		if(!isp[i])prime[++pcnt]=i;
		for(int j=1,x; j<=pcnt&&(x=prime[j]*i)<=n; ++j) {
			isp[prime[j]*i]=1;
			if(!(i%prime[j]))break;
		}
	}
}
int main() {
	int n,q;
	scanf("%d%d",&n,&q);
	init(n);
	for(int i=1,x; i<=q; ++i) {
		scanf("%d",&x);
		printf("%d\n",prime[x]);
	}
	return 0;
}

分解质因数

这个可以做到\(O(\sqrt{n})\)\(n\)分解

可以发现,大于\(\sqrt{n}\)的质因数最多有一个,且指数一定为\(1\)

不然它们乘起来就大过\(n\)了,所以我们可以只枚举\(\leq n\)的质因数。

先放代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=40;
int p[MAXN],c[MAXN],pcnt,n;
void work(int n) {
	for(int i=2; i*i<=n; ++i) {
		if(!(n%i)) {//枚举到一个质因数
			p[++pcnt]=i;//存下
			while(!(n%i)) {//计算指数
				n/=i;
				++c[pcnt];
			}
		}//由于比这个因子更小的质因子都被除掉了
      //所以这个因数一定是质因数
	}
	if(n>1) {//有大于根号n的质因数
		p[++pcnt]=n;//存下
		c[pcnt]=1;
	}
}
int main() {
	scanf("%d",&n);
	work(n);
	for(int i=1; i<=pcnt; ++i) {
		printf("%d %d\n",p[i],c[i]);
	}
	return 0;
}
posted @ 2021-03-10 17:50  mod998244353  阅读(51)  评论(0编辑  收藏  举报
Live2D