区间素数个数(min25筛)

题目大意

1 ∼ n 1\sim n 1n的素数个数。 1 ≤ n ≤ 1 0 11 1\leq n\leq 10^{11} 1n1011


前置知识:min25筛

f ( x ) = [ x ∈ p r i m e ] f(x)=[x\in prime] f(x)=[xprime],当 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>iprjknf(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;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值