2019.3.30 提高A组 T2 JZOJ 3188 找数

62 篇文章 0 订阅
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,p109


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 ansp2,根据这条性质,我们可以直接强行将 p p p的范围缩小到 1 0 9 ≈ 32500 \sqrt{10^9}\approx 32500 109 32500

也就是说,我们只需要筛出 1 ∼ 32500 1\sim 32500 132500的的素数

然后枚举 1 ∼ n 1\sim n 1n判断是否能被 ≤ 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 6p3,这样我们的常数就已经降低到五分之一了

可以发现 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(2pnp2×)


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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值