完全平方数的质因子的指数一定为偶数。
别问我为什么写
筛质数:埃氏筛法与线性筛法
线性筛法(又称欧拉筛法)
算法步骤
- 从小到大枚举每个数
• 如果当前数没划掉,必定是质数,记录该质数。
• 枚举已记录的质数(如果合数已越界则中断)
- 合数处理
• 条件i % p == 0,保证合数只被最小质因子划掉
• 若i是质数,则最多枚举到自身中断
• 若i是合数,则最多枚举到自身的最小质数中断
示例
• n=30
• i = 2:p[2];v[4];(2%2)break
• i = 3:p[3];v[6,9];(3%3)break
• i = 4:v[8];(4%2)break
• i = 5:p[5];v[10,15,25];(5%5)break
• i = 6:v[12];(6%2)break
• i = 7:p[7];v[14,21];(35>30)
• i = 8:v[16];(8%2)break
• i = 9:v[18,27];(9%3)break
• i = 10:v[20];(10%2)break
• i = 11:p[11];v[22];(33>30)
• i = 12:v[24];(12%2)break
• i = 16:(32>30)
代码实现
const int N = 100000010;
int vis[N]; // 划掉合数
int prim[N]; // 记录质数
int cnt; // 质数个数
void get_prim(int n) { // 线性筛法
for (int i = 2; i <= n; i++) {
if (!vis[i]) prim[++cnt] = i;
for (int j = 1; i * prim[j] <= n; j++) {
vis[i * prim[j]] = 1;
if (i % prim[j] == 0) break; // 越界中断
}
}
}
代码解释
• const int N = 100000010;:定义数组大小。
• int vis[N];:用于标记合数。
• int prim[N];:用于记录质数。
• int cnt;:记录质数的个数。
• void get_prim(int n):实现线性筛法的函数。
• for (int i = 2; i <= n; i++):从2开始遍历到n。
• if (!vis[i]) prim[++cnt] = i;:如果当前数未被标记为合数,则记录为质数。
• for (int j = 1; i * prim[j] <= n; j++):遍历所有质数因子。
• vis[i * prim[j]] = 1;:标记合数。
• if (i % prim[j] == 0) break;:如果当前数能被质数因子整除,则中断循环。
筛法求欧拉函数
欧拉函数的定义
1到n中与n互质的数的个数称为欧拉函数,记为φ(n)。
欧拉函数的性质
-
若p是质数,则φ§=p-1
-
若p是质数,则φ(pk)=(p-1)p(k-1)
-
积性函数:若gcd(m,n)=1,则φ(mn)=φ(m)φ(n)
欧拉函数的计算公式
由唯一分解定理 n=Π(从1到s)p_iα_i=p_1α_1 * p_2^α_2 * … * p_s^α_s,
得φ(n)=n * Π(从1到s)(p_i^(α_i-1) * (p_i-1))
筛法求欧拉函数
若i是质数,phi[i]=i-1。
在线性筛中,每个合数m都是被最小的质因子筛掉的。
设p_j是m的最小质因子,则m通过m=p_j * i筛掉。
-
若i能被p_j整除,则i包含了m的所有质因子。
例,φ(12)=φ(2 * 6)=2 * φ(6) -
若i不能被p_j整除,则i和p_j是互质的。
例,φ(75)=φ(3 * 25)=(3-1) * φ(25)
代码实现
const int N = 1000010;
int p[N], vis[N], cnt;
int phi[N];
void get_phi(int n) { // 筛法求欧拉函数
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
p[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; i * p[j] <= n; j++) {
int m = i * p[j];
vis[m] = 1;
if (i % p[j] == 0) {
phi[m] = p[j] * phi[i];
break;
} else {
phi[m] = (p[j] - 1) * phi[i];
}
}
}
}
示例
• n=30
• i=2:p[2],phi[2]=1
• i=3:p[3],phi[3]=2
• i=4:v[6],phi[6]=(2-1) * phi[3]=2
• i=5:p[5],phi[5]=4
• i=6:v[12],phi[12]=2 * phi[6]=4
• i=7:p[7],phi[7]=6
• i=8:v[16],phi[16]=2 * phi[8]=8
• i=9:v[18],phi[18]=(2-1) * phi[9]=6
• i=10:v[20],phi[20]=2 * phi[10]=8
• …
• i=16:(32>30)
筛法求约数和
问题描述
给定一个数 ( n ) (( n \leq 1 \times 10^6 )),输出从 1 到 ( n ) 每个数的约数和。
以16来举例子,其质因子为2指数为4.
所以其约数之和为(20+21+22+23+2^4)=31
这就是约数之和定理。
回顾一下 我们要做的就是将一个数求出他每一个质因子并记录其指数情况。之后将其拿出来先相加再做乘法就ok了
例子
- ( 12 = 2^2 \times 3^1 )
- ( f(12) = (1 + 2 + 4) \times (1 + 3) = 7 \times 4 = 28 )
算法步骤
- 初始化:
g[1] = f[1] = 1
- 对于每个 ( i ) 从 2 到 ( n ):
- 如果 ( i ) 是质数(即
vis[i]为 0),则:p[++cnt] = ig[i] = f[i] = i + 1
- 对于每个 ( j ) 从 1 到 ( n ):
- 计算 ( m = i \times p[j] )
- 标记 ( m ) 为已访问(
vis[m] = 1) - 如果 ( i ) 能被 ( p[j] ) 整除:
- ( g[m] = g[i] \times p[j] + 1 )
- ( f[m] = f[i] / g[i] \times g[m] )
- 跳出循环
- 否则:
- ( g[m] = p[j] + 1 )
- ( f[m] = f[i] \times g[m] )
- 如果 ( i ) 是质数(即
代码实现
const int N = 1000010;
int p[N], vis[N], cnt;
int g[N], f[N];
void get_f(int n) {
g[1] = f[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
p[++cnt] = i;
g[i] = f[i] = i + 1;
}
for (int j = 1; i * p[j] <= n; j++) {
int m = i * p[j];
vis[m] = 1;
if (i % p[j] == 0) {
g[m] = g[i] * p[j] + 1;
f[m] = f[i] / g[i] * g[m];
break;
} else {
g[m] = p[j] + 1;
f[m] = f[i] * g[m];
}
}
}
}
笔记总结
- 使用筛法求约数和,通过质数分解和乘法原理计算每个数的约数和。
- 算法复杂度较低,适用于大规模数据。
- 代码实现中,
g[i]表示 ( i ) 的最小质因子的 1 + ( p^1 ) + … + ( p^k ),f[i]表示 ( i ) 的约数和。
1634

被折叠的 条评论
为什么被折叠?



