D e s c r i p t i o n Description Description
找出第 n n n个最小素因子是 p p p的正整数,无解输出0
数据范围: n , p ≤ 1 0 9 n,p\leq 10^9 n,p≤109
S o l u t i o n Solution Solution
我可是那个试图推出规律的菜鸡呀。。。
分析题意,实际上就是要求第 n n n个能被 p p p整除当不能被小于 p p p的质数整除的数
首先推一下规律
当 n = 1 n=1 n=1时, a n s = p ans=p ans=p
当 n > 1 n>1 n>1时, a n s ≤ p 2 ans\leq p^2 ans≤p2,根据这条性质,我们可以直接强行将 p p p的范围缩小到 1 0 9 ≈ 32500 \sqrt{10^9}\approx 32500 109≈32500
也就是说,我们只需要筛出 1 ∼ 32500 1\sim 32500 1∼32500的的素数
然后枚举 1 ∼ n 1\sim n 1∼n判断是否能被 ≤ p \leq p ≤p的素数整除即可
但这样复杂度为 O ( n × 素 数 个 数 ) O(n\times 素数个数) O(n×素数个数),会 T T T,怎么办呢?
可以发现,能整除 p p p的数必然是 p p p的倍数,所以我们设置公差为 p p p
复杂度变成了 O ( n p × 素 数 个 数 ) O(\frac n p\times 素数个数) O(pn×素数个数),似乎还是有点不稳
我们发现, p = 2 p=2 p=2和 p = 3 p=3 p=3的情况都是可以手推的,为 2 p 2p 2p和 6 p − 3 6p-3 6p−3,这样我们的常数就已经降低到五分之一了
可以发现 p > 2 p>2 p>2时,枚举的数必然是个奇数,所以又可以减小一半的常数,这样就已经过了。
还能再优化!
从 p 2 p^2 p2开始循环!(可以证明大于2的素数的平方必然是奇数)
最终时间复杂度 O ( n − p 2 2 p × 质 数 个 数 ) O(\frac {n-p^2}{2p}\times 质数个数) O(2pn−p2×质数个数)
C o d e Code Code
#include<cstdio>
#include<algorithm>
#define INF 1000000000
using namespace std;int n,p,m,v[32501],prime[30001],k;
inline bool ok(int x)//判断能否被之前的素数整除
{
for(register int i=2;i<=k;i++) if(x%prime[i]==0) return false;
return true;
}
signed main()
{
scanf("%d%d",&n,&p);
if(n==1) return printf("%d",p)&0;
if(n>1&&(long long)p*p>INF) return putchar(48)&0;
for(register int i=2;i<=32500;i++)//线性筛素数
{
if(!v[i])
{
v[i]=i;prime[++m]=i;
}
for(register int j=1;j<=m;j++)
{
if(prime[j]>v[i]||prime[j]*i>32500) break;
v[i*prime[j]]=prime[j];
}
}
if(p==2)
{
if((long long)n*p<=INF) return printf("%d",n*p)&0;
return putchar(48)&0;
}
if(p==3)
{
if((long long)p+(n-1)*6<=INF) return printf("%d",p+(n-1)*6)&0;
return putchar(48)&0;
}
if(p==5)
{
n--;
for(register int i=p*p;i<=INF;i+=10)
if(i%3) if(!--n) return printf("%d",i)&0;
return putchar(48)&0;
}
if(p==7)
{
n--;
for(register int i=p*p;i<=INF;i+=14)
if((i%3)&&(i%5)) if(!--n) return printf("%d",i)&0;
return putchar(48)&0;
}
if(p==11)
{
n--;
for(register int i=p*p;i<=INF;i+=22)
if((i%3)&&(i%5)&&(i%7)) if(!--n) return printf("%d",i)&0;
return putchar(48)&0;
}
k=lower_bound(prime+1,prime+1+m,p)-(prime+1);//找出这是第几个素数
n--;//因为是从p^2开始循环的,所以要--
for(register int i=p*p;i<=INF;i+=2*p)
if(ok(i)) if(!--n) return printf("%d",i)&0;
return putchar(48)&0;
}