[数论] 欧拉筛法
前言
最近学数论,我是真的绝望,欧拉筛法也只能靠背代码勉强凑合凑合,但在我社CSQ大佬的帮助下,我理解到了其中神奇的奥妙
正题
欧拉筛法是一种可以筛出质数,欧拉函数,约数个数和约数和的筛法 那么我们就对这些问题逐一进行讲解
在这之前,我们先说几个东西:
1、每一个大于等于2的正整数
2、正整数
3、正整数
4、正整数
质数
思路
筛质数是一个相对简单的内容,在普通的筛法中,我们知道当一个数
但在欧拉筛法中,我们会进行一定的优化,那么外层循环枚举
如果
代码
inline void sieve(int x) {
for(reg int i = 2;i <= x;i ++) {
if(! vis[i])
prim[++ len] = i;
for(reg int j = 1;j <= len && i * prim[j] <= x;j ++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0)
break;
}
}
}
欧拉函数
思路
我们知道欧拉函数是这样的:
那么在之前筛质数的基础上,我们知道会进行两种判断,我们也根据这两种情况来求解:
1、
在
因为
可得
2、
我们把
因为
可得
代码
inline void sieve(int x) {
phi[1] = 1;
for(reg int i = 2;i <= x;i ++) {
if(! vis[i]) {
prim[++ len] = i;
phi[i] = i - 1; //因为欧拉函数代表小于这个数的且与这个数互质的数的个数,所以质数的欧拉函数为它本身减1
}
for(reg int j = 1;j <= len && i * prim[j] <= x;j ++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0) {
phi[i * prim[j]] = phi[i] * prim[j];
break;
}
phi[i * prim[j]] = phi[i] * (prim[j] - 1);
}
}
}
约数个数
思路
先摆公式
和之前的一样,我们分两种情况讨论:
1、
在
因为
可得
但是因为我们在用欧拉筛法求解时,并不知道
2、
也就是说,
因为
可得
代码
inline void sieve(int x) {
for(reg int i = 2;i <= x;i ++) {
if(! vis[i]) {
prim[++ len] = i;
d[i] = 2; //质数的约数只有1和它本身
sum[i] = 1;
}
for(reg int j = 1;j <= len && i * prim[j] <= x;j ++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0) {
sum[i * prim[j]] = sum[i] + 1;
d[i * prim[j]] = d[i] / (sum[i] + 1) * (sum[i] + 2);
break;
}
sum[i * prim[j]] = 1;
d[i * prim[j]] = d[i] * 2;
}
}
}
约数和
思路
公式在此:
同样,我们分两种情况:
1、
在
因为
此时我们用一个
2、
假设
因为
可得
代码
inline void sieve(int x) {
for(reg int i = 2;i <= x;i ++) {
if(! vis[i]) {
prim[++ len] = i;
psum[i] = s[i] = i + 1;
}
for(reg int j = 1;j <= len && i * prim[j] <= x;j ++) {
vis[i * prim[j]] = 1;
if(i % prim[j] == 0) {
psum[i * prim[j]] = psum[i] * prim[j] + 1;
s[i * prim[j]] = s[i] / psum[i] * psum[i * prim[j]]
break;
}
psum[i * prim[j]] = prim[j] + 1;
s[i * prim[j]] = s[i] * psum[i * prim[j]];
}
}
}