小奇学数论(loj 6235 区间素数个数)题解
min_25筛
昨天就想切这道题,结果晚上在划水,今天上午在考试,鸽到下午才做出来
本题做法有洲阁筛,min_25筛,某黑科技的素数筛等
注意到题目并未让我们输出具体有多少素数,所以可以用min_25筛
min_25筛的讲解在这里,自己看吧(捂脸
这里就借用这篇博客中的定义,谈此题的解法
设p为质数,区间质数个数为pl
定义f(p)=1,f(p*k)=0
求g(pl,n)
我们构造f'(i)=1,则函数f'满足:
- f'在质数处的取值与 f 相同。
- f′是完全积性函数。
- f'可以快速求前缀和。
故g的初值为
\(g(0,n)=n-1\)
g的递推式为
\(g(j,n)=\begin{cases}g(j-1,n)&p_j^2>n\\g(j-1,n)-(g(j,\lfloor\dfrac{n}{p_j}\rfloor)-(j-1))&p_j^2\le n\end{cases}\)
直接算即可。时间复杂度 \(O(\frac{N^{3/4}}{\log N})\)。
是时候给出此题的最短(最快?)代码了!
我,WXL,永不打表!!!
#include<bits/stdc++.h>
using namespace std;
#define go(i,a,b) for(int i=a;i<=b;++i)
#define fo(i,a) for(int i=0;i<a;++i)
#define int long long
#define id(x) ((x)<=sq?id1[x]:id2[n/(x)])
const int N=330000;
typedef int aray[N];
int n,pl,tot,sq;
aray p,g,val,id1,id2;
bool vis[N];
void init(){
sq=sqrt(n);
go(i,2,sq){
if(!vis[i]) p[pl++]=i;
for(int *j=p;j<p+pl&&(*j)*i<=sq;++j){
vis[(*j)*i]=1;
if(i%(*j)==0) break;
}
}
for(int i=1,j;i<=n;i=j+1){
j=n/(n/i);
val[++tot]=n/i;
n/i<=sq?id1[n/i]=tot:id2[j]=tot;
g[tot]=val[tot]-1;
}
}
signed main(){
//freopen("input.txt","r",stdin);
cin>>n;
init();
fo(i,pl)
go(j,1,tot){
if(p[i]*p[i]>val[j]) break;
g[j]-=g[id(val[j]/p[i])]-i;
}
printf("%lld",g[1]);
return 0;
}