GDOI2016模拟8.18找数

题目
找出第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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值