特别基础的数论
本文主要讲了线性筛素数、分解质因数算法。
线性筛素数
这里只讲欧拉筛
算法思路:若是枚举到一个数\(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;
}