题目描述
对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。
如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。
现在给定一个数N,你能求出不超过N的最大的反质数么?
输入输出格式
输入格式:
一个数N(1<=N<=2,000,000,000)。
输出格式:
不超过N的最大的反质数。
输入输出样例
输入样例#1: 复制
1000
输出样例#1: 复制
840
上课讲的一题,说是搜索+剪枝,一开始听不懂,很懵,上网找题解也看得有点不知所云,突然恍然大悟,我尽量表达地通俗易懂一些。
(1)首先想到直接暴力,但是如此就要找[1,n]的每一个数的因子个数,n<=2000000000,不用想肯定会爆。
(2)看了许多题解了解到几个性质
①对于某一个数,我们可以将其分解为质因子相乘的式子:2^k1*3^k2*5^k3……的形式(这个应该都懂)
然后这个数他的因子个数就是(k1+1)*(k2+1)*(k3+1)*(k4+1)……了
怎么去理解呢,我感性的理解就是,首先质数相乘出来的数肯定是唯一不会重复的,那么对于每一个质数就会尤其相对应的(k+1)的出现情况,那么每一个质因数不同的出现情况相乘,就是其因子的个数了,很好理解吧。
②如果这个数是一个反质数,那么他分解质因子后的式子一定满足k1>=k2>=k3……
这个怎么去看呢
首先由①可以知道如果k确定了(这里的k是k1,k2的集合),那么因子个数就确定了对吧,那么我们考虑这k1,k2……的排列,很明显,如果越大的ki给越小的质数,总体乘出来的数就是越小的。那么既然如此,其他比这个数大的数就肯定不满足反素数的性质了
好,有了上面的两个性质,就可以开始进行爆搜了,我来讲讲具体怎么搜,很多题解就是没说到,搞得我懵了好久。
如果不利用性质,我们就会暴力枚举每一个质因子的指数,然后看个数满不满足条件对吧,这样会超时
但是利用了这个性质,我们在枚举的时候,就要保证这个k的递减,那么爆搜的结果就极大地降低了。
!!!
注意:k递减得到的数也不一定是反素数,这也是为什么要爆搜的原因
n<=2*10^9所以只需枚举前十几个质数就ok,那个起始的最高指数,我是乱试的,感觉ok就行了。大了我也不知道为什么就得不出结果了,希望有大佬可以告诉我。
对于每一个爆搜得到的数,我要只需要记录出素数个数最多的那个数,这就是答案了, 因为答案肯定就包含在这些数中,如果它满足条件,那么因子个数肯定最多,我们就不需要考虑去验证它是不是反素数了。
附上代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int pri[11]={2,3,5,7,11,13,17,19,23,29,31},n,i;
long long ans;
int ans2;
void find(int x,int z,int g,long long lei){
if(lei > n) return;
if((g > ans2)||(g == ans2 && lei < ans)){
ans=lei; ans2=g;
}
long long c=1;
for(int i=1;i <= z; i++){
c*=pri[x];
find(x+1,i,g*(i+1),lei*c);
}
}
int main()
{
scanf("%d",&n);
ans=1;
ans=1;
find(0,23,1,1);
printf("%lld",ans);
return 0;
}