这道题比赛的时候没有写出来。。没有写出来是想不出如何验证这个数是不是符合题目要求的,写不出check函数。
参考大佬博客:https://blog.nowcoder.net/n/aa5ff9efa80440c897a9aaae4401a467
卡住我的两个点:
1、怎么分解出p的因数
可以通过一个for和while的嵌套:for循环从2开始枚举,每次枚举令p除到不能再被i整除(不含因数i)的时候位置,因为一个合数肯定是由比它小的素数相乘的来的,因此,for循环枚举i的时候,枚举到合数的时候他是会自动跳过的,因为已经枚举过这个合数的质因数了,所以现在的p自然也就没办法被合数i整除了。

inline void deal(ll p){//pr为map<ll,int>,记录每一个质因数的频数
    int cntt=0;
    for(int i=2;i*i<=p;i++){//一直枚举到
        while(p%i==0){//若p还含有因数i,则继续除下去
            pr[i]++;
            p/=i;
        }
    }
    if(p!=1){//这里是为了防止p=2这个特殊情况
        pr[p]++;
    }
}

2、怎么验证n符不符合条件
上面我们已经把p分解质因数了,这里,我们只需要验证n!中分解出来的因数个数能不能够大于p所具有的因数的个数。计算方法是:对于目前要检查的因数now,我们可以用一个while循环:当mid>=now的时候,说明,mid!当中还含有now或now的倍数,这时候,我们可以用一个while循环,sum+=mid/now求出一部分,后mid/now,知道mid<now.
举个栗子:
对于80!,我们可以数出来含有7的因数12个。
80!含有7的因数的项由7 14 21 28 35 42 49 56 63 70 77
除以一次7之后:1 2 3 4 5 6 7 8 9 10 11,sum=11,p=11
可以看到,里面仍然有一个7,再除一次,sum=12,p=1<7,结束循环。
这里可以这么想:含有因数7的一定是7的倍数。我们每一次除出来的结果,都是在这个数出在这个范围内由多少个7的倍数,每个数被数了一次(上面第一行的数都被数了一次)。n/=7后,还剩下的就是原来或以上次的数,这些数还含有一个7的因数,还能再除,以此类推,还有算……一直到n比7小,也就是为止。可以自己再好好感受下。

inline bool check(ll m){
    ll pos=0;
    for(auto it=pr.begin();it!=pr.end();it++){
        ll now=it->first;
        ll n=m,sum=0;
        while(n>=now){
            sum+=n/now;
            n/=now;
        }
        if(sum<it->second) return false;
    }
    return true;
}

完整代码:
二分就没什么好讲了。

#include <iostream>
#include <vector>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
map<ll,int> pr;
int cnt=0;

inline void deal(ll p){
    int cntt=0;
    for(int i=2;i*i<=p;i++){
        while(p%i==0){
            pr[i]++;
            p/=i;
        }
    }
    if(p!=1){
        pr[p]++;
    }
}

inline bool check(ll m){
    ll pos=0;
    for(auto it=pr.begin();it!=pr.end();it++){
        ll now=it->first;
        ll n=m,sum=0;
        while(n>=now){
            sum+=n/now;
            n/=now;
        }
        if(sum<it->second) return false;
    }
    return true;
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        pr.clear();
        ll p;
        scanf("%lld",&p);
        deal(p);
        ll ans=1;
        ll l=1,r=1e9;
        while(l<=r){
            ll m=(l+r)>>1;
            if(check(m)) ans=m,r=m-1;
            else l=m+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}