这道题比赛的时候没有写出来。。没有写出来是想不出如何验证这个数是不是符合题目要求的,写不出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;
}
京公网安备 11010502036488号