题解:P1221 最多因子数
求一个数的因数的个数
由算数基本定理
知,对于任意一个正整数
考虑
而
注:为了方便表述,下面用 r 表示原题中的 u .
暴力求解 时间复杂度 O((r-l)\sqrt{r})
首先容易想到暴力,枚举区间
#include<iostream>
using namespace std;
int l,r,ans,cnt;
inline void brute_force(){
int i,n,p,res,mul;
for(i=l;i<=r;i++){
n=i,res=1;
for(p=2;p*p<=n;p++){
mul=1;
while(!(n%p))
n/=p,mul++;
res*=mul;
}if(n!=1) res<<=1;
if(res>cnt) ans=i,cnt=res;
}
}int main(){
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(false);
cin>>l>>r,brute_force();
cout<<"Between "<<l<<" and "<<r<<", "<<ans;
cout<<" has a maximum of "<<cnt<<" divisors.";
return 0;
}
正解
上述的解法效率显然十分低下,因此考虑从最本质的定义入手,来求解因数最多的数。
还是考虑一个正整数
那么我们考虑枚举质数
这样子的话我们只需要先用欧拉筛筛出前
因此筛出质数后,即可使用 dfs 进行搜索,每一层枚举当前质因子的指数即可,部分代码实现如下。
void dfs(int x,int now,int mul){
int i,m,&p=prm[x];
if(l<=now&&now<=r){
if(mul>cnt||mul==cnt&&now<ans)
ans=now,cnt=mul;
}if(x==n) return;
for(i=0,m=1;now*m<=r;i++,m*=p)
dfs(x+1,now*m,mul*(i+1));
}
然而发现运行效率还是很低,这个时候就要使用搜索算法中的优化技巧——剪枝。
剪枝1
枚举到当前的数
具体的,我们考虑这样一件事,如果区间
又因为
由此我们得出了第一个剪枝方案:若
剪枝2
假如我们枚举到当前的数
显然,为了不让我们枚举的数
又因为我们当前枚举到的质因子为
由于后面质因子都要比当前的
剪枝3
考虑我们枚举到了质因子
而又因为我们是按照从小到大的顺序枚举每一个质因子
而在具体实现上有个小技巧,由前文知我们在每一层枚举的指数
小优化
由上文知我们当前枚举的质因子
因此我们可以考虑将指数
部分代码实现如下
inline int lg(int x,int y){
return log(y)/log(x)+eps;
}inline int qpow(int x,int y){
int ret=1;
while(y){
if(y&1) ret*=x;
x*=x,y>>=1;
}return ret;
}void dfs(int x,int now,int mul){
if(l<=now&&now<=r){
if(mul>cnt||mul==cnt&&now<ans)
ans=now,cnt=mul;
}if(x==n||(l-1)/now==r/now) return;
int m,&p=prm[x],i=lg(p,r/now);
if(!i||mul<<i<cnt) return;
for(m=qpow(p,i);~i;i--,m/=p)
dfs(x+1,now*m,mul*(i+1));
}
上文中就提到了我们讨论的是处理区间
因为在特殊的输入数据下(比如
由此,我们还需要结合上文给出的暴力求解方案。
我们考虑先筛出区间
不妨假设这个数最大的质因子
所以最坏的情况下(即
综上,在 dfs 搜索结束后,如果答案
这样,我们就既保证了算法的正确性,也在最大程度上优化了算法的复杂度,完整代码如下。
#include<cmath>
#include<bitset>
#include<iostream>
using namespace std;
constexpr int N=1e7;
constexpr double eps=1e-14;
int prm[N];
bitset<N>vis;
int i,j,l,r,n,ans,cnt;
inline int lg(int x,int y){
return log(y)/log(x)+eps;
}inline int qpow(int x,int y){
int ret=1;
while(y){
if(y&1) ret*=x;
x*=x,y>>=1;
}return ret;
}void dfs(int x,int now,int mul){
if(l<=now&&now<=r){
if(mul>cnt||mul==cnt&&now<ans)
ans=now,cnt=mul;
}if(x==n||(l-1)/now==r/now) return;
int m,&p=prm[x],i=lg(p,r/now);
if(!i||mul<<i<cnt) return;
for(m=qpow(p,i);~i;i--,m/=p)
dfs(x+1,now*m,mul*(i+1));
}inline void brute_force(){
int i,j,n,p,res,mul;
for(i=r;i>=l;i--){
n=i,res=1;
for(j=0;p*(p=prm[j])<=n;j++){
mul=1;
while(!(n%p))
n/=p,mul++;
res*=mul;
}if(n!=1) res<<=1;
if(res>=cnt) ans=i,cnt=res;
}
}int main(){
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(false);
for(i=2;i<N;i++){
if(!vis[i]) prm[n++]=i;
for(j=0;j<n&&i*prm[j]<N;j++){
vis.set(i*prm[j]);
if(!(i%prm[j])) break;
}
}cin>>l>>r,dfs(0,1,1);
if(cnt<25) brute_force();
cout<<"Between "<<l<<" and "<<r<<", "<<ans;
cout<<" has a maximum of "<<cnt<<" divisors.";
return 0;
}