AcWing 198. 反素数
. 反素数
一、题目描述
对于任何正整数 ,其约数的个数记作 ,例如 。
如果某个正整数 满足:对于任意的小于 的正整数 ,都有 ,则称 为反素数。
例如,整数 等都是反素数。
现在给定一个数 ,请求出不超过 的最大的反素数。
输入格式
一个正整数 。
输出格式
一个整数,表示不超过 的最大反素数。
数据范围
输入样例:
1000
输出样例:
840
二、解题思路
1、前置知识
设的唯一分解式:
- 约数个数公式
举栗子
约数个数=
- 约数和公式
Sigma(大写Σ,小写σ),是第十八个希腊字母。
举栗子
约数和
2、理解反素数
说白了,素数之所以称为素数,就是因为约数少,只有和自己。
反素数,也就可以理解为 :约数多,而且每个数的约数个数,都比小于自己的数字约数个数多。
3、反素数性质1
中最大的 反素数,就是 中约数个数最多的数中最小的一个。
- =约数个数,求最大的反素数,不就是在求哪个数的约数个数最多嘛~
- 如果不是最小的那一个,必然会出现
g(x)=g(i)
,与反素数的定义就矛盾了~
4、反素数性质2
联想约数个数公式:
一个数的约数个数,不与具体的质数因子相关,只与质数因子的幂次相关!
换言之,由于我们追求最小的数字,那么根据 贪心思想 ,我们知道:质因子越小,幂次越大,才可以保障最终的数字最小!!!
即
举个栗子
假设
则,交换和的次数即变成,这样交换约数个数相同,可,交换后该数变大了
5、反素数性质3
- 中任何数的不同质因子都不会超过个
因为:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int main() {
cout << INT_MAX << endl;
LL s = 1;
for (int i = 0; i <= 8; i++) s *= primes[i];
cout << s << endl;
s = 1;
for (int i = 0; i <= 9; i++) s *= primes[i];
cout << s << endl;
return 0;
}
输出结果:
2147483647
223092870
6469693230
- 所有质因数的指数总和最大不超过
因为.
根据上面一系列性质,我们得出了最简洁的思路,使用,尝试确定前九个质数的指数,然后满足指数单调递减,总乘积不超过,且同时记录约数的个数。
三、由小到大枚举
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
const int N = 2e9;
int n;
int primes[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; // 枚举的质数前9位,足够了
int mc, mx; // 最大反素数的约数个数,最大反素数值
/*
u:走到数组primes的第几位
last:前一个质数,我们取的指数是多少,当前位置取的质数的指数需要小于等于last
tmx: 当前路线上取得的最大反素数
tmc: 当前路线上取得的约数个数
*/
void dfs(int u, int last, int tmx, int tmc) {
if (u == 9) { // 走完全程
// ① 当前路径中约数个数大于最大约数个数,需要替换
// ② 当前路径中的约数个数等于最大约数个数,但是,当前路径的数值更小,也需要替换
if (tmc > mc || (tmc == mc && tmx < mx))
mc = tmc, mx = tmx;
return;
}
for (int i = 0; i <= last; i++) { // 幂次只能是小于等于前一个数的幂次
int k = pow(primes[u], i);
if (k * tmx > n) break; // 如果取当前这个素数的i次方,乘到原来的最大值后,大于n,那么此路径不通
/*
u+1:下一个素数位置
i: 当前u这个素数取了i次幂
tmx*k : 因为取了i次幂,所以,最大值变大了k倍
tmc*(i+1) : 根据约数个数公式,因为有了质数 primes[u]后,取了i次幂,那么约数和增加(i+1)倍
*/
dfs(u + 1, i, tmx * k, tmc * (i + 1));
}
}
signed main() {
cin >> n;
dfs(0, 30, 1, 1); // 从最小的质数2出发,最大的幂次不超过30
// n的最小取值是1,也就是小于等于1的范围内,最小的反素数是1,而且,约数的个数也是1
cout << mx << endl;
}
四、经验总结
在INT_MAX
范围内,最大的反素数拥有的约数个数是个 ,这个是整数范围内约数个数的极限值, 需要记住这个值,后面的做题当中可以当做常数使用!
输入,输出。
输入INT_MAX
,即,输出。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2015-05-20 TD的访问地址
2014-05-20 关于空间的改造方案
2013-05-20 现有的数据库与资源备份工作安排