【51Nod 1184】第N个质数

转发出处:http://blog.csdn.net/doyouseeman/article/details/54747260

Description

第1个质数是2,第3个质数是5,给出一个数N,求第N个质数。

Solution

很明显这个要二分答案。 
现在的主要问题就是求f[i]表示i里面有多少个质数。 
这里可以用个非正式的洲阁筛。 
设 
f[i]i  
p[i]i  
g[i][j]ip[1...j]  
那么先来看看f,首先我们知道一个数i可以被 i 以内的质数给筛掉,那么筛不掉的就是质数,所以有 f[i]=f[i]+g[i][f[i]] -1,因为1不是质数。 
然后p可以用线筛来解决。 
g[i][j]可以从g[i][j-1]推过来,但是g[i][j-1]还包括一些是p[j]的倍数的数: p[j],2p[j],3p[j],4p[j]...ip[j]p[j] ,所以就要排除掉 [1...ip[j]]p[1...j1] ,所以有 g[i][j]=g[i][j1]g[ip[j]][j1]  
但是只有这个还不行,时间卡不过去。 
我们考虑如何在根号时间内处理好。 
那么很明显要优化求g的时间。 
所以这里有两个优化: 
1、当 f[i]<=j ,那么g[i][j]=1,因为j包含了组成[2…i]的所有质数,那么除了1以外,其他的数都会被整数。 
2、如果不满足上面的条件,当 f[i]<=j ,那么g[i][j]=f[i]+1-j,因为[1…i]的数可以由 i 里所有的质数给筛掉,但是j包含了这些质数,那么只有除了这j个质数之外的质数,和1可以不被整除了。 
剩下的要考虑的就是空间与时间的问题,51Nod这道题不打表还跑得挺慢的。

代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=7500000+7;
typedef long long ll;
using namespace std;
ll i,j,k,t,n,m,ans;
ll r,mid,l;
int f[maxn],p[maxn];
bool bz[maxn];
ll g(ll n,int m){
    if(!m)return n;
    if(m==1)return n-n/2;
    if(n<=maxn-7){
        if(f[n]<=m)return 1;
        if(f[(int)sqrt(n)]<=m)return f[n]-m+1;
    }
    return g(n,m-1)-g(n/p[m],m-1);
}
bool pan(ll x){
    ll y=sqrt(x);
    return f[y]+g(x,f[y])-1>=n;
}
int main(){
    fo(i,2,maxn-7){
        if(!bz[i])p[++p[0]]=i;
        fo(j,1,p[0]){
            t=p[j]*i;
            if(t>maxn-7)break;
            bz[t]=1;if(i%p[j]==0)break;
        }
    }
    fo(i,2,maxn-7)f[i]=f[i-1]+(bz[i]==0);
    scanf("%d",&n);
    l=1,r=22801763489;
    while(l<r){
        mid=(l+r)/2;
        if(pan(mid))r=mid;else l=mid+1;
    }
    printf("%lld\n",l);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值