题目
找出第N个最小素因子是P的正整数
一行两个整数N和P(1<=N,P<=10^9),保证P是素数。
这题正难则反,若知道ans,我们可以通过知道有多少个数的最小质因数比p小,然后用ans减去来判断答案是否合法(若剩余的数≥N则ans偏大,否则偏小),这样我们可以二分答案。
对于判定,我们可以这样来:
方法一:由于ans比较大,所以我们用 n−−√ 以内且比p小的素数去筛掉 n−−√ 以内的不合法的数,这个可以容斥(也可以用欧拉定理或一开始线性筛完暴力跑一遍),然后对于 n−−√ 以外的,我们可以用 n−−√ 以内的素数去筛N、P,然后剩下 n−−√ 以外的质数,相减得到 n−−√ 以外的比p大的质数,合起来就可以了,由于是 n−−√ 的容斥或欧拉定理,时间还过得去(容斥中加上>1000000000就break掉的剪枝可以优化很多)(注意:这里的 n−−√ 是指 ans−−−√ 与 p√ 的最大值!!!)
方法二:和方法一差不多,但分两种情况:
1、暴力线筛扫一遍,空间方面允许开到5000000(有人开更大了..但我还是用这个),则允许的p≥200(不用二分答案)
2、当p<200时,我们可以沿用方法一的,二分答案,然后容斥原理(由于p比较小所以能用)
(当然,以
p√
为界限也可以)
方案二整体和方案一差不多,区别在于方案一把方案二统合起来二分了。
贴代码(方案二)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 5000000
#define MAXN 1000000000
int n,p,ans;
int a[N+1],d[N+1];
void init(){
scanf("%d %d",&n,&p);
}
void pre(int x){
a[1]=N;
for (int i=2;i<=x;i++){
if (!a[i])d[a[i]=++d[0]]=i;
for (int j=1;j<=d[0];j++)
if ((long long)d[j]*i>x)break;
else
a[i*d[j]]=j;
}
}
long long did(int x,int y,long long z){
long long s;
if (z>y)return 0;
s=y/z;
for (int i=x;i<=d[0];i++)
s-=did(i+1,y,z*d[i]);
return s;
}
void work(){
static int x,l,r,mid;
if (p<200){
pre(p-1);
l=1,r=MAXN/p;
while (l<=r){
if (did(1,mid=(l+r)/2,1)>=n)r=mid-1;else l=mid+1;
}
if (!l)l=1;
while ((long long)p*(l+1)<=MAXN&&did(1,l,1)<n)l++;
while ((long long)p*l>MAXN||(l&&did(1,l-1,1)>=n))l--;
if (did(1,l,1)==n)ans=l*p;
}else{
pre(N);
if (p<=N)
x=a[p];
else
x=N-1;
for (int i=1;i<=N;i++){
if ((long long)i*p>MAXN)return;
if (a[i]>=x){
n--;
if (!n){
ans=i*p;
return;
}
}
}
}
}
void write(){
printf("%d",ans);
}
int main(){
init();
work();
write();
return 0;
}