51Nod1184 第N个质数

作为作者今天唯一做出来的一道题,作者表示只能写这题的题解了……


题意如题目,就是求第 \(n\) 个质数,但是 \(n<=1e9\) ,所以我们必须使用小于线性的做法来做这题。因为 \(n\) 是质数的序号,我们不可能直接通过数学方式处理(黎曼猜想说不定可以?)。

我们可以先思考另外一个问题,即我们如何求 \(n\) 以内的素数个数。如果我们可以在 \(O(n)\) 以内处理出这个问题,那么再进行二分,就可可以解决这个问题了。

而如何在线性以内的时间处理小于等于 \(n\) 的素数个数,这里使用的是一种容斥原理的方法。

显然,我们可以使用线性筛处理出一定小范围内的素数个数,然后我们可以利用此来进行暴力。

我们设 \(f(x)\)\(1\)\(x\) 中素数的个数, \(pri(y)\) 为第 \(y\) 个素数, \(g(x,y)\)\(1\)\(x\) 中不被第 \(1\)\(y\) 个质数整除的数的个数。易得:

\[ g(i,j)= \begin{cases} (x+1)/2&y=1\\ 1&f(x)<=y\\ f(x)-y+1&f(\sqrt{x})<=y\\ g(x,y-1)-g(x/pri(y),y-1)&else\\ \end{cases}\]

这里我们可以解释一下,第一种情况十分显然,所有的奇数都不能被第一个素数整除。这里更重要的作用是作为边界。

第二种情况是如果 \(1\)\(x\) 中的素数个数比 \(y\) 还少,那么 \(y\) 个素数一定能将其中的所有数整除,除了 \(1\)

第三种情况是如果 \(1\)\(\sqrt{x}\) 中的素数比 \(y\) 还少,那么由情况二可得 \(1\)\(\sqrt{x}\) 中只有 \(1\) 是不能被这 \(y\) 个素数整除了,而剩余的 \(\sqrt{x}\)\(x\) 的情况,可知其中的数最多只有一个大于 \(\sqrt{x}\) 质因数,而其中没有小于等于 \(\sqrt{x}\) 的质因数的数只能是质数,所以只要求出 \(\sqrt{x}\)\(x\) 中质数的个数即可。

而第四中情况则是容斥原理,易得 \(1\)\(x\) 中不能被第 \(1\)\(y\) 个质数整除的数的个数等于 \(1\)\(x\) 中不能被第 \(1\)\((y-1)\) 个质数减去 \(1\)\(x\) 中不能被第 \(1\)\((y-1)\) 个素数整除但能被第 \(y\) 个素数整除的数的个数。而后者的个数,就是 \(1\)\((x/pri(y))\) 中不能被 \(1\)\((y-1)\) 个素数整除的数的个数,因为只要将 \(1\)\((x/pri(y))\) 的数乘上 \(pri(y)\) 就可以得到不能被第 \(1\)\((y-1)\) 个素数整除但能被第 \(pri(y)\) 个素数整除的数。

通过以上四种情况,我们可以写出一种时间复杂度比较优秀的递归(具体的复杂度本蒟蒻不会算),代码如下

ll g(ll x,int y)
{
    if(y==1)
    return (x+1)>>1;
    if(x<=(int)1e7&&f[x]<=y)
    return 1;
    if(x<=(int)1e7&&f[(int)sqrt(x)]<=y)
    return f[x]-y+1;
    return g(x,y-1)-g(x/pri[y-1],y-1);
}

但是,还有一个问题,我们知道了 \(g(x,y)\) 后对于我们计算 \(n\) 以内的质数有什么帮助呢?我们可以将整个数段分为 \(1\)\(\sqrt{n}\)\(\sqrt{n}\)\(n\) 。如果我们找出 \(1\)\(n\) 中不被第 \(1\)\(f(\sqrt{n})\) 个素数整除的数,同情况三, \(\sqrt{n}\)\(n\) 中的数最多只有一个大于 \(\sqrt{n}\) 质因数,而其中没有小于等于 \(\sqrt{n}\) 的质因数的数只能是质数,再加上我们暴力处理的 \(1\)\(\sqrt{n}\) 的质数个数,就可以得到答案了。即:

\[f(n)=f(\sqrt{n})+g(n,f(\sqrt{n}))-1 \]

附上完整代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll  n,l,r,mid,ans;
vector<int> pri;
int f[10000005];
bool vis[10000005];
ll g(ll x,int y)
{
    if(y==1)
    return (x+1)>>1;
    if(x<=(int)1e7&&f[x]<=y)
    return 1;
    if(x<=(int)1e7&&f[(int)sqrt(x)]<=y)
    return f[x]-y+1;
    return g(x,y-1)-g(x/pri[y-1],y-1);
}
ll find(ll x)
{
    return f[(int)sqrt(x)]+g(x,f[(int)sqrt(x)])-1;
}
signed main()
{
    cin>>n;
    vis[1]=true;
    for(int i=2;i<=1e7;++i)
    {
        if(!vis[i])
        pri.push_back(i);
        for(int j=0;j<pri.size();++j)
        {
            if((ll)i*pri[j]>1e7)
            break;
            vis[i*pri[j]]=true;
            if(i%pri[j]==0)
            break;
        }
    }
    for(int i=1;i<=1e7;++i)
    f[i]=f[i-1]+!vis[i];
    l=2;
    r=3e10;
    while(l<=r)
    {
        mid=(l+r)>>1;
        // printf("%lld %lld %lld\n",l,r,mid);
        if(find(mid)>=n)
        {
            r=mid-1;
            ans=mid;
        }
        else
        l=mid+1;
    }
    printf("%lld\n",ans);
}
posted @ 2020-07-21 18:57  Point_King  阅读(280)  评论(0编辑  收藏  举报