题目描述
原题来自:POI 2001
如果一个大于等于 1 的正整数 n,满足所有小于 n 且大于等于 1 的所有正整数的约数个数都小于 n 的约数个数,则 n 是一个反素数。譬如:1, 2, 4, 6, 12, 24,它们都是反素数。
请你计算不大于 n 的最大反素数。
输入
一行一个正整数 n。
输出
只包含一个整数,即不大于 n 的最大反素数。
样例输入
1000
样例输出
840
提示
数据范围与提示
对于 10% 的数据,1≤n≤10^3;
对于 40% 的数据,1≤n≤10^6;
对于 100% 的数据,1≤n≤2×10^9。
来源/分类
题解:
这题对我这种数学蒟蒻来说真是太难了
首先,这题需要用到两个定理:唯一分解定理以及因数个数定理
唯一分解定理:任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
这里p1<p2<p3<...<pn,且均为质数
因数个数定理:
因数个数定理可以套用上面的式子——
而N的因数个数就为:
总结一下,就是指数+1连乘
从公式可以看出——只有指数影响因数个数!
在唯一分解定理中可以看出指数放置的位置对于因数个数来说并不影响
举个肥肠简单的栗子:与的因数个数相同
因为所有小于n的数其因数个数都少于n,所以相同约数个数,数越小越好。
接下来可以运用一下贪心,得到一个策略:将尽量大的指数搭配在尽量小的质因数上
而数据范围小于,所以指数最大31,直接dfs即可!
但是这道题还有一个坑点:那就是所有变量最好都开long long(否则就像我一样,运行错误40%!)
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int prime[]={0,2,3,5,7,11,13,17,19,23,29,31};
ll n;
ll ans_num=0,ans_ges=0;
void dfs(ll now,ll ges,ll num,ll up)
{
if(ges>ans_ges)
{
ans_num=num;
ans_ges=ges;
}
else if(ges==ans_ges && num<ans_num) ans_num=num;
for(int i=1;i<=up;i++)
{
num*=prime[now];
if(num>n) return;
dfs(now+1,ges*(i+1),num,i);
}
}
int main()
{
scanf("%lld",&n);
dfs(1,1,1,31);
printf("%lld\n",ans_num);
return 0;
}