题目大意及模型转换
找出第N个最小素因子是P的正整数。
N,P<=10^9,如果结果超过10^9则输出0否则输出这个数。
超过10^9
我们先来处理结果超过10^9。
显然,对于一个质数p,第一个符合条件的是自己,第二个就是p*p。
我们发现p<=10^9,也就是说,n=1的情况所有质数都不会超过10^9。
超过的情况会从第二个开始。
那么首先可以知道,对于大于
109−−−√
的质数p,如果n>1即可说明要输出0。
然而小于
109−−−√
的也有超过10^9的情况。
不过现在,我们也就只需要考虑小于
109−−−√
的数了。
分类讨论
设f[i]表示i含有的最大质因数,这个显然可以筛出来。
但因为空间限制,我们只能筛一部分。
对于质数p,如果找到一个q满足f[q]>=p,那么p*f[q]就是满足条件的数。
我们知道,对于大质数,很容易超过10^9。
如果我们规定一个界限,超过这个界限的质数去枚举q,那么q显然不会很大。
我们不妨设这个界限为200,那么q的最大值便为
109200=5∗106
这个空间不会爆,我们可以筛出来。
对于小质数,该怎么办呢?
二分查找
我们这样想,假设枚举出一个q。
我们想知道1~q中有多少个满足f值大于等于p的。
其实等价于总数减去f值小于p的。
如果最后得到的结果恰好为n,那么p*q即为第n个满足条件的。
这个q显然可以二分答案。
至于那个个数,没有那么简单。
我们的想法是减去2的倍数个数,3的倍数个数等等等等。
但是6的倍数会被算两次。
好我们加回来,又发现30的倍数多了等等等。
因此我们要用容斥原理。
用小于p的所有质数进行组合,选一些这样的质数组成一个x,那么看q以内有多少个数是x的倍数,接下来如果我们选择了奇数个质数组成x,就减,否则加。
深搜组合?复杂度好像很大。
但实际上,组合出来的必须在q以内,所以复杂度并不会很大。
注意
p*1也满足条件。第一种情况下,请记得特判。
二分结果后,还要进行验证来决定是输出答案还是输出0。
参考程序
#include<cstdio>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int f[5000000],pri[3000000],i,j,k,l,r,mid,t,n,p,m,w,top;
long long xdl;
void dfs(int x,int y,int z){
if (x==w){
if (y!=1){
if (z%2) t+=mid/y;else t-=mid/y;
}
return;
}
long long xdl=pri[x]*y;
if (xdl<=mid) dfs(x+1,xdl,z+1);
dfs(x+1,y,z);
}
int main(){
m=floor(sqrt(1000000000));
scanf("%d%d",&n,&p);
if (p>m&&n>1) printf("0\n");
else if(p>m&&n==1) printf("%d\n",p);
else if(n==1) printf("%d\n",p);
else {
fo(i,2,5000000){
if (!f[i]){
pri[++top]=i;
f[i]=i;
fo(j,i,5000000/i)
if (!f[i*j])
f[i*j]=i;
}
}
if (p>200){
n--;
t=0;
fo(i,2,5000000){
if (f[i]>=p){
t++;
if (t==n){
xdl=p*i;
if (xdl>1000000000) printf("0\n");else printf("%d\n",xdl);
break;
}
}
}
}
else{
fo(i,1,top)
if (pri[i]==p) break;
w=i;
l=p;
r=1000000000/p;
while (l<r){
mid=(l+r)/2;
t=0;
dfs(1,1,0);
if (mid-t<n) l=mid+1;else r=mid;
}
t=0;
mid=l;
dfs(1,1,0);
if (l-t==n) printf("%d\n",l*p);else printf("0\n");
}
}
}