找数

题目大意及模型转换

找出第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=5106
这个空间不会爆,我们可以筛出来。
对于小质数,该怎么办呢?

二分查找

我们这样想,假设枚举出一个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");
        }
    }
}

你可能感兴趣的:(找数)