找数

来源:互联网 发布:json对象合并成一个 编辑:程序博客网 时间:2024/05/10 18:09

题目大意及模型转换

找出第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");        }    }}
0 0