文章目录
线性筛法
1. 算法分析
关键思想:每个数字都被它的最小质因数筛掉
1.1 线性筛求素数
- i % p j = 0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj 是 i i i 的最小质因数,因此 i ∗ 质 数 i*质数 i∗质数 的最小质因子也是 p j p_j pj,在 i i i 递增的时候也会被筛掉,因此不需要在这里判断,可以break
- i % p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0,说明 p j p_j pj 是 i ∗ p j i*p_j i∗pj 的最小质因数,因此 p j p_j pj 可以筛掉 i ∗ p j i * pj i∗pj
1.2 线性筛求欧拉函数
- i % p j = 0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj 是 i i i 的最小质因数, φ ( i ∗ p j ) = i ∗ p j ∗ ∏ i = 1 s p i − 1 p i = p j ∗ ( i ∗ ∏ i = 1 s p i − 1 p i ) = p j ∗ φ ( i ) \varphi(i * p_j) = i * p_j * \prod_{i=1}^{s}\frac{p_i-1}{p_i} = p_j * (i * \prod_{i=1}^{s}\frac{p_i-1}{p_i}) = p_j * \varphi(i) φ(i∗pj)=i∗pj∗∏i=1spipi−1=pj∗(i∗∏i=1spipi−1)=pj∗φ(i)
- i % p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0, 说明 i i i 和 p j p_j pj 互质, 那么 φ ( i ∗ p j ) = φ ( i ) ∗ φ ( p j ) = φ ( i ) ∗ ( p j − 1 ) \varphi(i*p_j) = \varphi(i) * \varphi(p_j) = \varphi(i) * (p_j - 1) φ(i∗pj)=φ(i)∗φ(pj)=φ(i)∗(pj−1)
1.3 线性筛求莫比乌斯函数
莫比乌斯函数
μ
(
n
)
\mu(n)
μ(n)
$\mu(n)= \begin{cases} 1&n=1\ 0&n\text{ 含有平方因子}\ (-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\ \end{cases} $
- i % p j = 0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj的幂次为2,因此 μ ( i ∗ p j ) = 0 \mu(i*p_j) = 0 μ(i∗pj)=0
- i % p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0, 说明 i i i 和 p j p_j pj 互质, μ ( i ∗ p j ) = μ ( i ) ∗ μ ( p j ) = − μ ( i ) \mu(i * p_j) = \mu(i) * \mu(p_j) = -\mu(i) μ(i∗pj)=μ(i)∗μ(pj)=−μ(i)
1.4 线性筛求约数个数
d(i) 表示 i 的约数个数,num[i] 表示 i 的最小素因子的个数
- i % p j ! = 0 i \% p_j != 0 i%pj!=0, 则 p j p_j pj是 i ∗ p j i * p_j i∗pj的最小质因数,且 p j p_j pj这个质因数第一次出现在 i ∗ p j i*p_j i∗pj内,次数为1,则 n u m [ i ∗ p j ] = 1 , d [ i ∗ p j ] = d [ i ] ∗ 2 num[i * p_j] = 1, d[i * p_j] = d[i] * 2 num[i∗pj]=1,d[i∗pj]=d[i]∗2
- i % p j = 0 i \% p_j = 0 i%pj=0, 则 p j p_j pj在 i ∗ p j i*p_j i∗pj中的次数加一,对于约数个数,先除以原来的 n u m [ i ] + 1 num[i] + 1 num[i]+1,再乘上新的 n u m [ i ] + 2 num[i] + 2 num[i]+2,则 n u m [ i ∗ p j ] = n u m [ i ] + 1 , d [ i ∗ p j ] = d [ i ] / ( n u m [ i ] + 1 ) ∗ ( n u m [ i ] + 2 ) = d [ i ] / n u m [ i ∗ p j ] ∗ ( n u m [ i ∗ p j ] + 1 ) num[i * p_j] = num[i] + 1, d[i * p_j] = d[i] / (num[i] + 1) * (num[i] + 2) = d[i] / num[i * p_j] * (num[i * p_j] + 1) num[i∗pj]=num[i]+1,d[i∗pj]=d[i]/(num[i]+1)∗(num[i]+2)=d[i]/num[i∗pj]∗(num[i∗pj]+1)
1.5 线性筛求约束和
f[i]表示i的约数和,gi表示i的最小质因数的 p 0 + p 1 + . . . + p k p^0+p^1+...+p^k p0+p1+...+pk
- i % p j ! = 0 i \% p_j != 0 i%pj!=0, 则 p j p_j pj是 i ∗ p j i * p_j i∗pj的最小质因数,且 p j p_j pj这个质因数第一次出现在 i ∗ p j i * p_j i∗pj内,次数为1,则 f [ i ∗ p j ] = f [ i ] ∗ f [ p j ] , g [ i ∗ p j ] = 1 + p j f[i * p_j] = f[i] * f[p_j], g[i * p_j] = 1 + p_j f[i∗pj]=f[i]∗f[pj],g[i∗pj]=1+pj
- i % p j = 0 i \% p_j = 0 i%pj=0, 则 p j p_j pj在 i ∗ p j i * p_j i∗pj中的次数加一,则 g [ i ∗ p j ] = g [ i ] ∗ p j + 1 , f [ i ∗ p j ] = f [ i ] / g [ i ] ∗ g [ i ∗ p j ] g[i * p_j] = g[i] * p_j + 1, f[i * p_j] = f[i] / g[i] * g[i * p_j] g[i∗pj]=g[i]∗pj+1,f[i∗pj]=f[i]/g[i]∗g[i∗pj]
2. 模板
2.1 线性筛求素数
// 线性筛素数(以下把prime[j]这个数简写为pj)
// 如何保证线性:一个合数只能被筛掉一次
// 如何保证一个数字只被筛掉一次:这个数字只被它的最小质因数筛掉,当它的其他质因数想要筛掉它时,将无法进行筛除操作
void get_prime(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i; // 如果这个数字没有被记录,那么这个数字必然为素数,记录一下
for (int j = 0; prime[j] <= n/i; ++j) {
st[prime[j] * i] = true; // 筛掉pj*i这个合数
if (i % prime[j] == 0) break; // i%pj==0,说明pj是i的最小素因子,因此i*素数的最小素因子也是pj,在i递增的时候也会被筛掉,因此不需要在这里判断
}
}
}
2.2 线性筛求欧拉函数
void init(int n) {
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i, phi[i] = i - 1; // i是质数,phi[i] = i - 1
for (int j = 0; prime[j] <= n / i; ++j) {
st[prime[j] * i] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j]; // i % pj == 0, phi[i * pj] = phi[i] * pj
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1); // i % pj != 0, phi[i * pj] = phi[i] * (pj - 1)
}
}
return;
}
2.3 线性筛求莫比乌斯函数
// 预处理得到莫比乌斯函数
void init(int n) {
mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i, mu[i] = -1; // i是质数,mu[i] = -1
for (int j = 0; prime[j] <= n / i; ++j) {
st[prime[j] * i] = 1;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0; // i % pj==0, mu[i*pj] = 0
break;
}
mu[i * prime[j]] = -mu[i]; // i % pj!=0, mu[i * pj] = -mu[i]
}
}
return;
}
2.4 线性筛求约数个数
// d(i) 表示 i 的约数个数,num[i] 表示 i 的最小素因子的个数
void init(int n) {
d[1] = 1;
for (int i = 2; i <= n; ++i) {
if(!st[i]) prime[cnt++] = i, d[i] = 2, num[i] = 1; //i是质数,d[i] = 2, num[i] = 1
for (int j = 0; prime[j] <= n / i; ++j) {
st[i * prime[j]] = 1;
if (i % prime[j] == 0) { // i % pj == 0, num[i * pj] = num[i] + 1, d[i *pj] = d[i] / num[i * pj] * (num[i * pj] + 1)
num[i * prime[j]] = num[i] + 1;
d[i * prime[j]] = d[i] / num[i * prime[j]] * (num[i * prime[j]] + 1);
break;
}
// i % pj != 0, num[i * pj] = 1, d[i * pj] = d[i] * 2
num[i * prime[j]] = 1;
d[i * prime[j]] = d[i] * 2;
}
}
return;
}
2.5 线性筛求约数和
// f[i] 表示i的约数和,g[i]表示i的最小质因数的p^0 + p^1 + ... + p^k
void init(int n) {
g[1] = f[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i, g[i] = 1 + i, f[i] = i + 1; // i是质数,g[i] = 1 + i, f[i] = i + 1
for (int j = 0; prime[j] <= n / i; ++j) {
st[i * prime[j]] = 1;
if (i % prime[j] == 0) { // i % pj == 0, g[i * pj] = g[i] * pj + 1, f[i * pj] = f[i] / g[i] * g[i * pj]
g[i * prime[j]] = g[i] * prime[j] + 1;
f[i * prime[j]] = f[i] / g[i] * g[i * prime[j]];
break;
}
// i % pj != 0, f[i * pj] = f[i] * f[pj], g[i * pj] = 1 + pj
f[i * prime[j]] = f[i] * f[prime[j]];
g[i * prime[j]] = 1 + prime[j];
}
}
return ;
}