[LOJ6235]區間素數個數


題目大意:
  給定$n(n\leq10^{11})$,求$\pi(n)$。

思路:
  計算$\pi$函數有$O(n^{\frac23})$的Lehmer算法,這里考慮$O(\frac{n^{\frac34}}{\ln n})$的洲閣篩。
  我們可以將答案分為$\leq\sqrt n$的質數個數和$>\sqrt n$的質數個數。
  其中$\leq\sqrt n$的質數個數可以線性篩預處理,而$>\sqrt n$的質數個數相當於用$\leq\sqrt n$的質數篩這$n$個數后剩下的數的個數。
  若用$f[i][j]$表示$1\sim j$中與前$i$個數互質的數的個數,則轉移方程為$f[i][j]=f[i-1][j]-f[i-1][\lfloor\frac j{p_i}\rfloor]$。$\pi(n)\sim\frac n{\ln n}$,$j$有$\sqrt n$種取值,時間復雜度$O\left(\frac n{\ln\sqrt n}\right)=O\left(\frac n{\ln n}\right)$。
  當$p_{i+1}>j$時,$f[i][j]=1$。所以當$p_i>\frac j{p_i}$時,轉移方程變為$f[i][j]=f[i-1][j]-1$。
  因此對於每一個$j$,只需計算$p_i^2\leq j$的$f[i][j]$即可。對於$p_i^2>j$的$j$,可以記錄最后一步的$i$是多少,轉移的時候把那些$1$一起減掉。答案就是一開始線性篩求出的$\leq\sqrt n$的質數個數+用$\leq\sqrt n$篩完剩下的數。注意篩完除了那些$>\sqrt n$的質數,還會剩下$1$,因此最后要把$1$去掉。
  時間復雜度$O\left(\frac{n^\frac34}{\ln n}\right)$。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<algorithm>
 5 typedef long long int64;
 6 inline int64 getint() {
 7     register char ch;
 8     while(!isdigit(ch=getchar()));
 9     register int64 x=ch^'0';
10     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
11     return x;
12 }
13 const int LIM=316228,P=27294;
14 bool vis[LIM];
15 int lim,p[P],sum[LIM],last[LIM*2],cnt;
16 int64 val[LIM*2],f[LIM*2];
17 inline void sieve() {
18     for(register int i=2;i<=lim;i++) {
19         if(!vis[i]) p[++p[0]]=i;
20         sum[i]=sum[i-1]+!vis[i];
21         for(register int j=1;j<=p[0]&&i*p[j]<=lim;j++) {
22             vis[i*p[j]]=true;
23             if(i%p[j]==0) break;
24         }
25     }
26 }
27 int main() {
28     const int64 n=getint();
29     lim=sqrt(n);
30     sieve();
31     for(register int64 i=1;i<=n;i=n/(n/i)+1) {
32         val[++cnt]=n/i;
33     }
34     std::reverse(&val[1],&val[cnt]+1);
35     std::copy(&val[1],&val[cnt]+1,&f[1]);
36     for(register int i=1;i<=p[0];i++) {
37         for(register int j=cnt;j;j--) {
38             const int64 k=val[j]/p[i],pos=k<=lim?k:cnt+1-n/k;
39             if(k<p[i]) break;
40             f[j]-=f[pos]+last[pos]-i+1;
41             last[j]=i;
42         }
43     }
44     printf("%lld\n",sum[lim]+f[cnt]-1);
45     return 0;
46 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM