51Nod1184 第N个质数
作为作者今天唯一做出来的一道题,作者表示只能写这题的题解了……
题意如题目,就是求第 \(n\) 个质数,但是 \(n<=1e9\) ,所以我们必须使用小于线性的做法来做这题。因为 \(n\) 是质数的序号,我们不可能直接通过数学方式处理(黎曼猜想说不定可以?)。
我们可以先思考另外一个问题,即我们如何求 \(n\) 以内的素数个数。如果我们可以在 \(O(n)\) 以内处理出这个问题,那么再进行二分,就可可以解决这个问题了。
而如何在线性以内的时间处理小于等于 \(n\) 的素数个数,这里使用的是一种容斥原理的方法。
显然,我们可以使用线性筛处理出一定小范围内的素数个数,然后我们可以利用此来进行暴力。
我们设 \(f(x)\) 为 \(1\)~\(x\) 中素数的个数, \(pri(y)\) 为第 \(y\) 个素数, \(g(x,y)\) 为 \(1\)~\(x\) 中不被第 \(1\)~\(y\) 个质数整除的数的个数。易得:
这里我们可以解释一下,第一种情况十分显然,所有的奇数都不能被第一个素数整除。这里更重要的作用是作为边界。
第二种情况是如果 \(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}\) 的质数个数,就可以得到答案了。即:
附上完整代码:
#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);
}