题目大意
求 1 ∼ n 1\sim n 1∼n的素数个数。 1 ≤ n ≤ 1 0 11 1\leq n\leq 10^{11} 1≤n≤1011
前置知识:min25筛
令 f ( x ) = [ x ∈ p r i m e ] f(x)=[x\in prime] f(x)=[x∈prime],当 x x x为质数时, f ( x ) = 1 f(x)=1 f(x)=1;当 x x x不为质数时, f ( x ) = 0 f(x)=0 f(x)=0。此时题意变为求 f ( x ) f(x) f(x)的前缀和,我们可以用min25筛。
令 f ′ ( x ) = x f'(x)=x f′(x)=x, f ′ ( x ) f'(x) f′(x)为积性函数且在 f ( x ) f(x) f(x)为质数时与 f ( x ) f(x) f(x)相等。用 f ′ ( x ) f'(x) f′(x),求出min25筛中的 g g g函数,然后来求min25筛中的 S S S函数。以下是min25筛中 S S S函数的递推式:
S ( n , i ) = g ( n , ∣ p r ( n ) ∣ ) − g ( p r i , ∣ p r ( n ) ∣ ) + ∑ j > i ∑ p r j k ≤ n f ( p r j k ) × ( S ( ⌊ n p r j k ⌋ , j ) + [ k > 1 ] ) S(n,i)=g(n,|pr(n)|)-g(pr_i,|pr(n)|)+\sum\limits_{j>i}\sum\limits_{pr_j^k\leq n}f(pr_j^k)\times (S(\lfloor\dfrac{n}{pr_j^k}\rfloor,j)+[k>1]) S(n,i)=g(n,∣pr(n)∣)−g(pri,∣pr(n)∣)+j>i∑prjk≤n∑f(prjk)×(S(⌊prjkn⌋,j)+[k>1])
我们发现在最后一项,当 k = 1 k=1 k=1时, S ( ⌊ n p r j k ⌋ , j ) + [ k > 1 ] = 0 S(\lfloor\dfrac{n}{pr_j^k}\rfloor,j)+[k>1]=0 S(⌊prjkn⌋,j)+[k>1]=0;当 k > 1 k>1 k>1时, p r j k pr_j^k prjk为合数, f ( p r j k ) = 0 f(pr_j^k)=0 f(prjk)=0。所以最后一项为0,可省去。则 S S S的递推式变为
S ( n , i ) = g ( n , ∣ p r ( n ) ∣ ) − g ( p r i , ∣ p r ( n ) ∣ ) S(n,i)=g(n,|pr(n)|)-g(pr_i,|pr(n)|) S(n,i)=g(n,∣pr(n)∣)−g(pri,∣pr(n)∣)
求出 S S S函数即可,最后的答案为 S ( n , 0 ) S(n,0) S(n,0)(本题中 f ( 1 ) = 0 f(1)=0 f(1)=0,不用加1)。
时间复杂度为 O ( n 3 4 ln n ) O(\dfrac{n^{\frac 34}}{\ln n}) O(lnnn43)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1000000;
int p1,vt,z[N+5],pr[N+5];
long long n,x,v[N+5],f[2][N+5],s[N+5],g[N+5];
void dd(){
for(int i=2;i<=N;i++){
if(!z[i]){
pr[++p1]=i;
s[p1]=p1;
}
for(int j=1;j<=p1&&i*pr[j]<=N;j++){
z[i*pr[j]]=1;
if(i%pr[j]==0) break;
}
}
}
void init(){
x=sqrt(n)+1;
dd();
for(long long l=1,r;l<=n;l=r+1){
r=n/(n/l);
v[++vt]=n/l;
if(n/l<=x) f[0][n/l]=vt;
else f[1][l]=vt;
long long t=n/l;
g[vt]=t-1;
}
long long w;
for(int i=1;i<=p1;i++){
w=1ll*pr[i]*pr[i];
long long t;
for(int j=1;j<=vt&&w<=v[j];j++){
t=v[j]/pr[i];
if(t<=x) t=f[0][t];
else t=f[1][n/t];
g[j]-=g[t]-s[i-1];
}
}
}
long long S(long long p,int q){
if(pr[q]>=p) return 0;
long long td=(p<=x?f[0][p]:f[1][n/p]);
long long re=g[td]-s[q];
return re;
}
int main()
{
scanf("%lld",&n);
init();
if(n==1) printf("1");
else printf("%lld\n",S(n,0));
return 0;
}