反素数 Antiprime(dfs)
原题来自:POI 2001
如果一个大于等于 1 的正整数 n,满足所有小于 n 且大于等于 1 的所有正整数的约数个数都小于 n 的约数个数,则 n 是一个反素数。譬如:1, 2, 4, 6, 12, 24,它们都是反素数。
请你计算不大于 n 的最大反素数。
输入格式
一行一个正整数 n。
输出格式
只包含一个整数,即不大于 n 的最大反素数。
样例
Input
1000
Output
840
数据范围与提示
对于 10% 的数据,1≤n≤103;
对于 40% 的数据,1≤n≤106;
对于 100% 的数据,1≤n≤2×109。
思路: 用到了唯一分解定理和因数个数定理
唯一分解定理:
任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
N=p1^a1 * p2^a2 * p3^a3...这里p1<p2<p3<...<pn,且均为质数
因数个数定理:
还是上面那个式子,N的因数个数 = (a1+1) * (a2+1) * ... *(an+1);
反素数的本质就是尽可能地让因子数量最大,但是数值最小。
下面来解释一下这句话
以我们的题为例,求不大于n的最大反素数,令它为x:
- x一定是小于n的约数最多的数(根据定义很容易就能看出来)
- 当约数个数相同时,x一定是最小的数(举个例子,a,b(a<b)的约数个数相同,若我们选取b为反素数,则会不满足f(a)<f(b),f(x)为x的因数个数,所以我们的x一定是最小的数)
那如何选取最小的数呢,指数递减即可
AC代码:
#include<stdio.h>
#include<string.h>
#define ll long long
#include<algorithm>
using namespace std;
ll n,maxx=0,ans=0;
//maxx记录总因数的最大值,ans记录对应的数值
ll p[11]={0,2,3,5,7,11,13,17,19,23,29};
//deep 当前枚举到第几个质数了
//cur 从开始到现在共有多少因数
//num 枚举到此组成的数
//up 当前质数的最大指数
//n最大取到2e9,2*3*5*7*11*13*17*19*23>2e9,所以枚举到此即可
void dfs(ll deep,ll cur,ll num,ll up)
{
if(deep>10) return ;
if(cur>maxx||(cur==maxx&&num<ans))//目前因数总个数>maxx,或因数个数相等,数值较小,更新maxx,ans
maxx=cur,ans=num;
for(int i=1;i<=up;i++)//枚举
{
num*=p[deep];
if(num>n) return ;
dfs(deep+1,(i+1)*cur,num,i);
}
}
int main()
{
scanf("%lld",&n);
dfs(1,1,1,31);
//枚举从2开始,2^31 > 2e9
printf("%lld\n",ans);
return 0;
}