题解:P7884 【模板】Meissel-Lehmer - 指南

P7884

简介

Meissel-Lehmer 是一种素数筛,适用于 n n n 超大的范围。

时间复杂度对比

算法名称预处理时间复杂度单次查询时间复杂度空间复杂度适用场景
Meissel-Lehmer O ( n 2 / 3 / log ⁡ n ) O(n^{2/3}/\log n) O(n2/3/logn) O ( 1 ) O(1) O(1) O ( n 2 / 3 ) O(n^{2/3}) O(n2/3)超大范围素数计数( n ≥ 10 12 n \geq 10^{12} n1012
分段筛(Segment) O ( n log ⁡ log ⁡ n ) O(n \log \log n) O(nloglogn) O ( n ) O(\sqrt{n}) O(n)中等范围素数生成( n ≤ 10 8 n \leq 10^8 n108
欧拉筛(Euler) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)小范围素数预处理( n ≤ 10 7 n \leq 10^7 n107

前置知识

P3383 欧拉筛

欧拉筛可以以 O ( N ) O(N) O(N) 的方式求出区间 [ 1 , N ] [1,N] [1,N] 内的素数。

定义

  • p x p_{x} px 表示第 x x x 个素数。
  • π ( x ) \pi(x) π(x) 表示 [ 1 , x ] [1,x] [1,x] 内的素数个数。
  • d p x , k dp_{x,k} dpx,k 表示 [ 1 , x ] [1,x] [1,x]不被前 k k k 个素数整除的数的个数。(包含 1 1 1

定理推导

定理一:Legendre 公式(基础拆分)

对于任意满足 k = π ( n ) k=\pi(\sqrt{n}) k=π(n) n n n k k k,有: π ( n ) = k + ( d p n , k − 1 ) \pi(n)=k+(dp_{n,k}-1) π(n)=k+(dpn,k1)

证明

一:所有素数 p i p_{i} pi 满足 ( p k < p i ≤ n p_{k}<p_{i}\le n pk<pin) 的数包含于 d p n , k dp_{n,k} dpn,k

因为 p i p_{i} pi 为素数,所以不会被前 k k k 个素数整除,满足 d p n , k dp_{n,k} dpn,k 的定义,得证。

二: d p n , k dp_{n,k} dpn,k 中除 1 1 1 外,其余数均满足 ( p k < i ≤ n p_{k}<i\le n pk<in)

考虑反证:设 m m m 存在于符合 d p n , k dp_{n,k} dpn,k 的定义且不满足 ( p k < m ≤ n p_{k}<m\le n pk<mn)。
因为 m m m 是合数,存在正整数 a , b > 1 a,b>1 a,b>1 满足 m = a × b m=a\times b m=a×b,因为 k = π ( n ) k=\pi(\sqrt{n}) k=π(n),所以有 a × b = m    ⟹    min ⁡ ( a , b ) ≤ n a\times b=m \implies \min{(a,b)}\le\sqrt{n} a×b=mmin(a,b)n
又因为 a , b > 1 a,b>1 a,b>1,所以 min ⁡ ( a , b ) = p j \min{(a,b)}=p_{j} min(a,b)=pj j ≤ k j\le k jk),与假设矛盾,故得证。

三:最终证明

由第二步得: d p n , k dp_{n,k} dpn,k 中除 1 1 1 外,其余数均满足 ( p k < p i ≤ n p_{k}<p_{i}\le n pk<pin) 。
C C C 为大于 p k p_{k} pk 的素数数量和。
显然有:

  • C = d p n , k − 1 C=dp_{n,k}-1 C=dpn,k1
  • π ( n ) = k + C \pi(n)=k+C π(n)=k+C 等量代换得: π ( n ) = k + d p n , k − 1 \pi(n)=k+dp_{n,k}-1 π(n)=k+dpn,k1
    得证。

定理二:如何计算 d p n , k dp_{n,k} dpn,k

可以通过容斥原理递归计算。
我们有:
不被前 k k k 个素数整除的数 = = = 不被前 k − 1 k−1 k1 个素数整除的数 − - p k p_k pk 整除但不被前 k − 1 k−1 k1 个素数整除的数。
而“被 p k p_k pk 整除但不被前 k − 1 k−1 k1 个素数整除的数” 等价于“ p k p_{k} pk 乘以不被前 k − 1 k−1 k1 个素数整除的数(且 ≤ x / p k \le x/p_k x/pk)”,故数量为 d p x / p k , k − 1 dp_{x/p_k,k−1} dpx/pk,k1
故有:

d p ( x , k ) = { x ( k = 0 ) 1 ( p k > x ) d p x , k = π ( x ) − k + 1 ( p k 2 > x ) d p x , k = d p x , k − 1 − d p ⌊ x / p k ⌋ , k − 1 dp(x,k)=\begin{cases} x&(k=0)\\ 1&(p_k>x)\\ dp_{x,k}=\pi(x)-k+1&(p_k^2>x)\\ dp_{x,k}=dp_{x,k-1}-dp_{\lfloor x/p_k\rfloor,k-1} \end{cases} dp(x,k)=x1dpx,k=π(x)k+1dpx,k=dpx,k1dpx/pk,k1(k=0)(pk>x)(pk2>x)

证明

对于任意 x , k ≥ 1 x,k\ge 1 x,k1,有 d p x , k = d p x , k − 1 − d p ⌊ x / p k ⌋ , k − 1 dp_{x,k}=dp_{x,k-1}-dp_{\lfloor x/p_k\rfloor,k-1} dpx,k=dpx,k1dpx/pk,k1
设:

  • S = { 1 ≤ m ≤ x ∣ m  不被前  k − 1  个素数整除  } ,则  ∣ S ∣ = d p x , k − 1 S=\{1≤m≤x|m\text{ 不被前 }k−1\text{ 个素数整除 }\},\text{则 }|S|=dp_{x,k−1} S={1mxm不被前k1个素数整除}S=dpx,k1
  • T = { 1 ≤ m ≤ x ∣ m  不被前  k  个素数整除 } ,则  ∣ T ∣ = d p x , k T=\{1≤m≤x|m\text{ 不被前 }k\text{ 个素数整除}\},\text{则 }|T|=dp_{x,k} T={1mxm不被前k个素数整除}T=dpx,k
第一步:分析 S S S T T T 的关系

k k k 个素数是前 k − 1 k−1 k1 个素数加 p k p_k pk,因此“不被前 k k k 个素数整除” 等价于“不被前 k − 1 k−1 k1 个素数整除,且不被 p k p_k pk 整除”。
有: ∣ T ∣ = ∣ S ∣ − ∣ { m ∈ S ∣ m  被  p k  整除 } ∣ |T|=|S|-|\{m\in S|m \text{ 被 }p_k\text{ 整除} \}| T=S{mSmpk整除}

第二步:证明 ∣ { m ∈ S ∣ m  被  p k  整除 } ∣ = d p ⌊ x / p k ⌋ , k − 1 |\{m\in S|m \text{ 被 }p_k\text{ 整除} \}|=dp_{\lfloor x/p_k\rfloor,k-1} {mSmpk整除}=dpx/pk,k1

m ∈ S m\in S mS m m m p k p_k pk 整除,则存在整数 t t t 使得 m = p k × t m=p_k\times t m=pk×t
因为 m ≤ x m\le x mx 所以 t ≤ ⌊ x / p k ⌋ t\le \lfloor x/p_k\rfloor tx/pk;又因为 m ∈ S m\in S mS,所以不被前 k − 1 k-1 k1 素数整除,得 t t t 不被前 k − 1 k-1 k1 个素数整除。(因为 p k p_k pk 与前 k − 1 k-1 k1 个素数互质,假设 t t t p i ( i < k ) p_i(i<k) pi(i<k) 整除,则 m = p k × t m=p_k\times t m=pk×t 也被 p i p_i pi 整除,与 m ∈ S m\in S mS 矛盾,故假设不成立)。
因为 t ≤ ⌊ x / p k ⌋ t\le\lfloor x/p_k\rfloor tx/pk t t t 不被前 k − 1 k-1 k1 个素数整除,则 m = p k × t ≤ x m=p_k\times t\le x m=pk×tx,且 m m m 不被前 k − 1 k-1 k1 个素数整除,得出 m ∈ S m\in S mS m m m p k p_k pk 整除。
因此 S S S 中被 p k p_k pk 整除的数与 1 ≤ t ≤ ⌊ x / p k ⌋ 1\le t\le\lfloor x/ p_k\rfloor 1tx/pk t t t 不被前 k − 1 k-1 k1 个素数整除的数是映射的,也就是两者的个数相等。
因为 t t t 不被前 k − 1 k-1 k1 个素数整除的数为 d p x , k dp_{x,k} dpx,k,所以: ∣ { m ∈ S ∣ m  被  p k  整除 } ∣ = d p ⌊ x / p k ⌋ , k − 1 |\{m\in S|m \text{ 被 }p_k\text{ 整除} \}|=dp_{\lfloor x/p_k\rfloor,k-1} {mSmpk整除}=dpx/pk,k1
故得证。


根据定义,有:
d p x , k = ∣ S ∣ − ∣ { m ∈ S ∣ m  被  p k  整除 } ∣ dp_{x,k}=|S|-|\{m\in S|m \text{ 被 }p_k\text{ 整除} \}| dpx,k=S{mSmpk整除}
故得: d p x , k = d p x , k − 1 − d p ⌊ x / p k ⌋ , k − 1 dp_{x,k}=dp_{x,k-1}-dp_{\lfloor x/p_k\rfloor,k-1} dpx,k=dpx,k1dpx/pk,k1
公式得证。

定理三:最终公式

Q ( n , k ) Q(n,k) Q(n,k) 表示为大于 k k k 的两个素数的乘积小于 n n n 的个数。
n n n 很大时,直接计算 d p x , k dp_{x,k} dpx,k 是很慢的,所以引入阈值 a = π ( n 1 / 3 ) a=\pi(n^{1/3}) a=π(n1/3),可以将 π ( n ) \pi(n) π(n) 分为三部分:

π ( n ) = π ( a ) + ( d p n , a − 1 ) − Q ( n , a ) \pi(n)=\pi(a) + (dp_{n,a}-1) - Q(n,a) π(n)=π(a)+(dpn,a1)Q(n,a)

接下来介绍各部分的含义:

  • π ( a ) \pi(a) π(a) [ 1 , a ] [1,a] [1,a] 中的素数个数。
  • d p n , a − 1 dp_{n,a}-1 dpn,a1
    • 不被前 a a a 个素数整除的数的个数减 1,包含大于 a a a 的素数和 Q ( n , a ) Q(n,a) Q(n,a)
  • − Q ( n , a ) -Q(n,a) Q(n,a)
    • 通过递归计算 d p n , a − 1 − ( π ( n ) − a ) dp_{n,a}-1-(\pi(n)-a) dpn,a1(π(n)a) 得到( d p n , a − 1 dp_{n,a}-1 dpn,a1 是不被前 a a a 个素数整除的数的个数,减去 π ( n ) − a \pi(n)-a π(n)a 个素数,剩余的为 Q ( n , a ) Q(n,a) Q(n,a))。 Q ( n , a ) = Σ i = a + 1 π ( n ) ( π ( n / p i ) − i + 1 ) Q(n,a) = \varSigma_{i=a+1}^{\pi(\sqrt{n})}(\pi(n/p_i)-i+1) Q(n,a)=Σi=a+1π(n)(π(n/pi)i+1)

证明

首先明确命题:
a = π ( n 1 / 3 ) a=\pi(n^{1/3}) a=π(n1/3) b = π ( n ) b=\pi(\sqrt{n}) b=π(n),则

π ( n ) = π ( a ) + ( d p n , a − 1 ) − Q ( n , a ) \pi(n)=\pi(a) + (dp_{n,a}-1) - Q(n,a) π(n)=π(a)+(dpn,a1)Q(n,a)

我们首先对 π ( n ) \pi(n) π(n) 进行拆分:

  • 第一类:对于 p i ≤ a p_i\le a pia 的素数,个数为 π ( a ) \pi(a) π(a)
  • 第二类:对于 a < p i ≤ n a<p_i\le n a<pin 的素数,设其个数为 D D D,则 π ( n ) = π ( a ) + D \pi(n)=\pi(a)+D π(n)=π(a)+D,故需要证明 D = ( d p n , a − 1 ) − Q ( n , a ) D=(dp_{n,a}-1)-Q(n,a) D=(dpn,a1)Q(n,a)

现在来分析第二类素数的性质:
对于类 2 2 2 中的素数 q ( q > a ) q(q>a) q(q>a),考虑任意合数 m = q × r ( r ≥ q m=q\times r(r\ge q m=q×r(rq,保证 q q q m m m 的最小因子 ) ) ),则 m ≤ n    ⟹    r ≤ ⌊ n / q ⌋ m\le n\implies r\le\lfloor n/q\rfloor mnrn/q
由于 q > a q>a q>a,得到 q > π ( n 1 / 3 ) q>\pi(n^{1/3}) q>π(n1/3),因 p π ( x ) > x p_{\pi(x)}>x pπ(x)>x,故 m = q × r ≥ q 2 > n 2 / 3 m=q\times r\ge q^2>n^{2/3} m=q×rq2>n2/3,且 r ≤ ⌊ n / q ⌋ < n 2 / 3 r\le\lfloor n/q\rfloor<n^{2/3} rn/q<n2/3
我们继续细分第二类质数对应的合数贡献:

  • r ≤ a r\le a ra 时,此时 m = q × r m=q\times r m=q×r 的最小质因子为 r r r,对应的, q q q 需要满足 q ≤ ⌊ n / r ⌋ q\le\lfloor n/r\rfloor qn/r q > r q>r q>r
  • r > a r>a r>a 时,此时 m = q × r m=q\times r m=q×r 的最小质因子为 q q q,这类合数即为 Q ( n , a ) Q(n,a) Q(n,a)

接下来分别计算两种情况的贡献
第一种

考虑 a < p i ≤ b a<p_i\le b a<pib,有 p i ≤ n 1 / 2 p_i\le n^{1/2} pin1/2(因为 b = π ( n ) b=\pi(\sqrt{n}) b=π(n)):

  • 对于每个 p i ( a < i ≤ b ) p_i(a<i\le b) pi(a<ib),对应的 q q q 需满足 q > p i q>p_i q>pi q ≤ ⌊ n / p i ⌋ q\le\lfloor n/p_i\rfloor qn/pi
  • 这类 q q q 的个数是 π ( ⌊ n / p i ⌋ ) − i + 1 \pi(\lfloor n/p_i\rfloor)-i+1 π(⌊n/pi⌋)i+1
    因为当 q = p i q=p_i q=pi 时, m = p i 2 m=p_i^2 m=pi2,但 p i > a = π ( n 1 / 3 ) > n 1 / 3 p_i>a=\pi(n^{1/3})>n^{1/3} pi>a=π(n1/3)>n1/3,故 p i 2 > n 2 / 3 p_i^2>n^{2/3} pi2>n2/3,但 p i ≤ n 1 / 2 p_i\le n^{1/2} pin1/2 等价于 p i 2 ≤ n p_i^2\le n pi2n,此时 p i 2 ≤ n p_i^2\le n pi2n,此时 q = p i q=p_i q=pi 也需计入,所以实际个数要加 1 1 1
    要对所以的 i = a + 1 i=a+1 i=a+1 b b b 求和,所以第一种对于的素数个数为 Σ i = a + 1 b ( π ( ⌊ n / p i ⌋ ) − i + 1 ) \varSigma_{i=a+1}^{b}(\pi(\lfloor n/p_i\rfloor)-i+1) Σi=a+1b(π(⌊n/pi⌋)i+1)
第二种

定义: m = q × r m=q\times r m=q×r 是“双素数乘积”。
同时,第二类的素数 q q q 满足“不是双素数乘积” 与“不被前 a a a 个素数整除”,结合 d p n , a dp_{n,a} dpn,a 的定义,有:

d p n , a − 1 = Q ( n , a ) +  第二类的素数个数  dp_{n,a}-1=Q(n,a)+\text{ 第二类的素数个数 } dpn,a1=Q(n,a)+第二类的素数个数

又因为( d p n , a − 1 dp_{n,a}−1 dpn,a1 是剔除 1 1 1 后的不被前 a a a 个素数整除的数,包含第二类素数和双素数乘积),所以有第二类素数个数 = ( d p n , a − 1 ) − Q ( n , a ) =(dp_{n,a}-1)-Q(n,a) =(dpn,a1)Q(n,a)
结合第一种情况,第二类素数个数 D D D 等于第一种个数 + + + 第二种对应的质数贡献,所以有:

D = Σ i = a + 1 b ( π ( ⌊ n / p i ⌋ ) − i + 1 ) − Q ( n , a ) D=\varSigma_{i=a+1}^{b}(\pi(\lfloor n/p_i\rfloor)-i+1)-Q(n,a) D=Σi=a+1b(π(⌊n/pi⌋)i+1)Q(n,a)

故得证。

实现思路

预处理小范围素数,再递归实现 d p dp dp π ( n ) \pi(n) π(n)

明确定义

变量

  • p i p_i pi 表示第 i i i 个素数。
  • i p i ip_i ipi 表是第 i i i 个数是否是素数。
  • g i g_i gi 表示 [ 1 , i ] [1,i] [1,i] 中有多少个素数。
  • f i , j f_{i,j} fi,j 表示小于 i i i 且不被前 j j j 个素数整除的个数(也就是 d p i , j dp_{i,j} dpi,j 的预处理版本)。

定义常数部分

  • N N N 表示欧拉筛处理的范围上限。
  • M X MX MX 表示 f i , j f_{i,j} fi,j 的第一维的大小。
  • M Y MY MY 表示 f i , j f_{i,j} fi,j 的第二维的大小。

一:欧拉筛

这个可以先套一下模板,但同时我们要维护 g g g f f f 数组。
对于 g g g 数组,采用类似前缀和的方式维护:

for(int i=1;i<N;i++)g[i]=g[i-1]+!ip[i];

对于 f f f 数组,可以采用之前 d p dp dp 的定义,有:

for(int i=1;i<=MX;i++)f[i][0]=i;
for(int i=1;i<=MX;i++)for(int j=1;j<=MY;j++)f[i][j]=f[i][j-1]-f[i/p[j]][j-1];

剩下的都是模板了,完整代码:

void shai(){
for(int i=2;i<N;i++){
if(!ip[i])p[++tot]=i;
for(int j=1;j<=tot&&p[j]*i<N;j++){
ip[i*p[j]]=1;
if(i%p[j]==0)break;
}
}
for(int i=1;i<N;i++)g[i]=g[i-1]+!ip[i];
for(int i=1;i<=MX;i++)f[i][0]=i;
for(int i=1;i<=MX;i++)for(int j=1;j<=MY;j++)f[i][j]=f[i][j-1]-f[i/p[j]][j-1];
}

d p i , j dp_{i,j} dpi,j 的计算

直接储存 d p i , j dp_{i,j} dpi,j 是存不下的,我们可以考虑预处理小部分。
对于 d p i , j dp_{i,j} dpi,j,有四种情况:

情况返回值判定条件解释
1 f i , j f_{i,j} fi,j i ≤ M X  且  j ≤ M Y i \le MX\text{ 且 }j \le MY iMXjMY在预处理范围内。
2 i i i ! i  或  ! j !i\text{ 或 }!j !i!j到达边界。
3 max ⁡ { 0 , g i − j } \max\{0,g_i-j\} max{0,gij} i < N  且  1 × p j × p j ≥ i i<N\text{ 且 }1\times p_j\times p_j\ge i i<N1×pj×pji剩余数均为素数。
4 d p i , j − 1 − d p i / p j , j − 1 dp_{i,j-1}-dp_{i/p_j,j-1} dpi,j1dpi/pj,j1不满足上述条件。上文 d p i , j dp_{i,j} dpi,j 的计算公式。

对于特殊情况的解释:当 p j 2 ≥ i p_{j}^2\ge i pj2i 时,小于 i i i 且不被前 j j j 个素数整除的数只能是素数(或 1 1 1),而 g i g_i gi 是小于 i i i 的素数总数,减去前 j j j 个素数(已被排除),得到结果。
基于此,得出以下代码:

long long dp(long long i,long long j){
if(i<=MX&&j<=MY)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);
}

P ( N ) P(N) P(N) 的计算 (其实就是计算 π ( n ) \pi(n) π(n))

当然,如果在预处理范围内就直接返回了。
定义 s u m = d p n , m + m − 1 sum=dp_{n,m}+m-1 sum=dpn,m+m1,也就是 d p n , m dp_{n,m} dpn,m 是小于 n n n 且不被前 m m m 个素数整除的数,加 m − 1 m-1 m1 是补上前 m − 1 m-1 m1 个素数(因前 m m m 个素数中排除了第 m m m 个),再减去多计算的合数,代码:

long long P(long long n){
if(n<N)return g[n]-1;
long long m=g[(long long)(pow(n,1./3))]-1,sum=dp(n,m)+m-1;
for(m++;
1ll*p[m]*p[m]<=n;m++)sum-=P(n/p[m])-P(p[m])+1;
return sum;
}

完整代码

#include<bits/stdc++.h>
  using namespace std;
  const int N=8e6+10,MX=1.8e6,MY=60;
  long long n;
  int f[MX+5][MY+5],p[N/10],ip[N],g[N],tot;
  void shai(){
  for(int i=2;i<N;i++){
  if(!ip[i])p[++tot]=i;
  for(int j=1;j<=tot&&p[j]*i<N;j++){
  ip[i*p[j]]=1;
  if(i%p[j]==0)break;
  }
  }
  for(int i=1;i<N;i++)g[i]=g[i-1]+!ip[i];
  for(int i=1;i<=MX;i++)f[i][0]=i;
  for(int i=1;i<=MX;i++)for(int j=1;j<=MY;j++)f[i][j]=f[i][j-1]-f[i/p[j]][j-1];
  }
  long long dp(long long i,long long j){
  if(i<=MX&&j<=MY)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);
  }
  long long P(long long n){
  if(n<N)return g[n]-1;
  long long m=g[(long long)(pow(n,1./3))]-1,sum=dp(n,m)+m-1;
  for(m++;
  1ll*p[m]*p[m]<=n;m++)sum-=P(n/p[m])-P(p[m])+1;
  return sum;
  }
  int main(){
  shai();
  long long n;
  cin>>n;
  cout<<
  P(n);
  return 0;
  }

后记

学了好久啊!
锐评我的代码:先放个记录,可以看到,代码的内存刚好卡线(评测机波动分分钟给你 MLE 了),所以请不要直接交(真有那样的人吗),可以尝试改小 N N N M X MX MX M Y MY MY 的范围再交。(或者拼运气?)
这是本人第一次写黑题题解,难免会有错误,希望大家指正。
最后,感谢观看!

posted @ 2025-09-03 22:17  yfceshi  阅读(15)  评论(0)    收藏  举报