[luogu p1221][codevs 1032]最多因子数
题目描述
数学家们喜欢各种类型的有奇怪特性的数。例如,他们认为945是一个有趣的数,因为它是第一个所有约数之和大于本身的奇数。
为了帮助他们寻找有趣的数,你将写一个程序扫描一定范围内的数,并确定在此范围内约数个数最多的那个数。不幸的是,这个数和给定的范围的都比较大,用简单的方法寻找可能需要较多的运行时间。所以请确定你的算法能在几秒内完成最大范围内的扫描。
输入输出格式
输入格式:
只有一行,给出扫描的范围,由下界L和上界U确定。满足2≤L≤U≤1000000000。
输出格式:
对于给定的范围,输出该范围内约数个数D最多的数P。若有多个,则输出最小的那个。请输出“Between L and U,P has a maximum of D divisors.”,其中L,U,P和D的含义同前面所述。
输入输出样例 别点复制,没用的:)
输入样例#1: 复制
1000 2000
输出样例#1: 复制
Between 1000 and 2000, 1680 has a maximum of 40 divisors.
这题的思路嘛。。。。
其实就是爆搜。
首先我们要知道,一个数X可以被分解为X=2^m1∗3^m2∗5^m3∗...∗n^mk
相信聪明的大家都能理解(注:n为质数)
当分解完后,X的个数为(m1+1)∗(m2+1)∗(m3+1)∗...∗(mk+1)
根据乘法原理,选1个,2个……m1个,为啥是m1+1?还可以不选haha
我们只需把质数预处理出来,然后暴力填m1,m2,m3,……,mk即可
加上一点小剪枝
代码:(卡常大法好)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 10005;
const int INF = 1000000007;
ll _[N],prime[N],size,ansp=INF,ansd,anss[N],l,r;
//p 素数 2 3 5 7 (第几个质因子)
//all已经搜索到的k的因子的乘积
//last上一个的p的指数
inline void dfs(int p,ll all,ll last){
if(l<=all&&all<=r){//满足要求
int o=1;
for(register int i=1;i<p;i++){//算出约数个数
o=o*(anss[i]+1);
}
if(o>ansd||(o==ansd&&all<ansp)){//最小
ansd=o;
ansp=all;
}
}
if(all>ansp) return;//最优性剪枝
ll lin[100]={0};
lin[0]=1;
for(register int i=1;i<=last;i++) lin[i]=lin[i-1]*prime[p];//预处理
for(register int i=last;i>=1;i--){//从后往前,更高效
anss[p]=i;
dfs(p+1,all*lin[i],i);
}
}
void bao_li_chu_qi_ji(){//只可会意,不可言传
for(int i=l;i<=r;i++){
int ret=0;
for(int j=1;j*j<=i;j++){
if(i%j==0) ret+=2;
if(j*j==i) ret--;
}
if(ret>ansd){
ansp=i;
ansd=ret;
}
}
}
int main(){
for(register int i=2;i<=sqrt(N);i++){
for(register int j=2;j*i<N;j++){
_[i*j]=1;
}
}
for(register int i=2;i<=N;i++){
if(!_[i]) prime[++size]=i;
}
scanf("%lld%lld",&l,&r);
ll w=log(r);
if(r-l<5000) bao_li_chu_qi_ji();//只可会意,不可言传
else dfs(1,1,w);
printf("Between %lld and %lld, %lld has a maximum of %lld divisors.\n",l,r,ansp,ansd);
return 0;
}
跑得还挺快的。。