Codeforces 300 E(数论)
传送门
题意:
让你找到一个最小的使得能够式子整除,并输出。
题目分析:
非常棒的一个题目!
首先,因为要求得一个最小的满足条件的值,因此,我们不妨可以想到可以使用二分。而对于这个问题,倘若能够被整除,则可知,也可以被整除,由此可知我们所求的答案具有一定的单调性,因此我们可以用二分算出答案。
对于整除性的问题有一个套路就是,我们可以把需要求的值进行素因数分解,分别统计每一个素因子的个数,最后我们只需要在二分答案的同时,将二分的值同时也进行素因数分解。最后我们只需要判断两次的素因子的个数进而进行二分边界的转移。
但是在这个题中,上述的做法的瓶颈在于我们不能用比较优的时间分解所有数的阶乘的质因数。因此此时需要部分优化。
我们可以设数组为这个数的出现次数。那么我们可以根据所给的统计出他们分别的出现次数。继而我们可以从后往前枚举所有的数,倘若有值,则根据阶乘的性质,很显然都应该增加次。(即如果为1,则、、必定也会出现一次)。
统计完每个数的出现次数之后,我们就可开始分解质因数。首先我们发现,如果一个数为素数则当前的答案即是最终的结果。而倘若当前的数为合数,则证明该数能够被分解,那么我们就将它分解为,其中为该数的最小质因数;同时我们需要把分解了的数和的出现次数分别加上,(因为要满足阶乘,则在和也必定出现次)
至此,我们就可以用的时间复杂度线性的求出所有质因数的出现次数。
之后我们只需要进行二分即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+10;
bool isprime[maxn];
int minp[maxn],k,tot;
int prime[maxn];
ll sum=0;
void getPrime(){
memset(isprime,1,sizeof(isprime));
for(int i=2;i<maxn;i++){
if(isprime[i]){
minp[i]=i,prime[tot++]=i;
for(int j=2*i;j<maxn;j+=i){
if(isprime[j]) minp[j]=i;
isprime[j]=false;
}
}
}
}
int a[maxn];
ll cnt[maxn];
ll res[maxn];
bool judge(ll mid){
memset(res,0,sizeof(res));
for(int i=0;i<tot;i++){
ll t=prime[i];
while(mid/t){
res[prime[i]]+=mid/t;
t*=prime[i];
}
}
for(int i=0;i<tot;i++){
if(cnt[prime[i]]>res[prime[i]]) return false;
}
return true;
}
int main()
{
int n;
getPrime();
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=a[i];
cnt[a[i]]++;
}
for(int i=maxn-1;i>=2;i--){
if(cnt[i]) cnt[i-1]+=cnt[i];
}
for(int i=maxn-1;i>=2;i--){
if(!isprime[i]){
cnt[i/minp[i]]+=cnt[i];
cnt[minp[i]]+=cnt[i];
}
}
ll l=0,r=sum;
ll ans=0;
while(l+1<r){
ll mid=(l+r)>>1;
if(judge(mid)) r=mid;
else l=mid;
}
cout<<r<<endl;
}