题解:P7884 【模板】Meissel-Lehmer
一道 Meissel-Lehmer 算法的模板题洛谷题解里没有 Meissel-Lehmer,大悲啊。
这里介绍 Meissel-Lehmer 算法。
先线性筛算出前大于等于
然后对于求
接下来不难发现,现在筛剩下的
计算这类由两个素数相乘的数的个数很简单,直接考虑枚举小的素数,去计算另外一个素数的方案数即可,假设当前选取了小素数
结合一下上述,可得出
公式前面减一是因为
然后考虑如何求 dp 数组,考虑转移。考虑计算最后一个素数的贡献,发现其贡献是其倍数中没有被前面素数筛掉的数的个数,发现可以再利用 dp 数组,所以转移为
考虑边界情况,显然的是
这样一个基础的 Meissel-Lehmer 算法原理讲解就完成了。
接下来是实现,对于 dp 数组,可以预处理出一部分,例如
由于不断递归了
const int N = 8e6 + 10;
const int MI = 1.8e6;
const int MJ = 60;
ll n;
int f[MI + 5][MJ + 5];
int p[N / 10], ip[N], g[N], cnt;
void init() {
FOR(i, 2, N - 1) {
if(!ip[i]) {
p[++cnt] = i;
}
for(int j = 1; j <= cnt && p[j] * i < N; j++) {
ip[i * p[j]] = 1;
if(i % p[j] == 0) {
break;
}
}
}
// 警惕!这里的 g 数组表示的 pi(n) 含有 1,是 pi(n)+1
FOR(i, 1, N - 1) g[i] = g[i - 1] + !ip[i];
FOR(i, 1, MI) f[i][0] = i;
FOR(i, 1, MI) FOR(j, 1, MJ)
f[i][j] = f[i][j - 1] - f[i / p[j]][j - 1];
}
ll dp(ll i, ll j) {
if(i <= MI && j <= MJ) return f[i][j];
if(!i || !j) return i;
if(i < N && 1ll * p[j] * p[j] >= i) return max(0ll, g[i] - j);
return dp(i, j - 1) - dp(i / p[j], j - 1);
}
ll pi(ll n) {
if(n < N) return g[n] - 1;
ll sn = pow(n, 1. / 3);
ll m = g[sn] - 1;
ll res = dp(n, m) + m - 1;
for(m++; 1ll * p[m] * p[m] <= n; m++)
res -= pi(n / p[m]) - pi(p[m]) + 1; // 继续递归
return res;
}