##[$AcWing$ $198$. 反素数](https://www.acwing.com/problem/content/description/200/)
### 一、题目描述
对于任何正整数 $x$,其约数的个数记作 $g(x)$,例如 $g(1)=1、g(6)=4$。
如果某个正整数 $x$ 满足:对于任意的小于 $x$ 的正整数 $i$,都有 $g(x)>g(i)$,则称 $x$ 为反素数。
例如,整数 $1,2,4,6$ 等都是反素数。
现在给定一个数 $N$,请求出不超过 $N$ 的最大的反素数。
**输入格式**
一个正整数 $N$。
**输出格式**
一个整数,表示不超过 $N$ 的最大反素数。
**数据范围**
$1≤N≤2∗10^9$
**输入样例**:
```cpp {.line-numbers}
1000
```
**输出样例**:
```cpp {.line-numbers}
840
```
### 二、解题思路
#### 1、前置知识
设$N$的唯一分解式:
$$N=P_1^{c_1}P_2^{c_2}...P_k^{c_k}$$
[知识总结](https://www.cnblogs.com/littlehb/p/15214618.html)
* 约数个数公式
$d(N)=(c_1+1)\times (c_2+1)\times ... \times (c_k+1)$
**举栗子**
> $180=2^2∗3^2∗5$
> 约数个数=$(1+2)∗(1+2)∗(1+1)=18$
* 约数和公式
$\sigma(n)=({p_1}^{0}+{p_1}^{1}+{p_1}^{2}+...+{p_1}^{c_1})({p_2}^{0}+{p_2}^{1}+{p_2}^{2}+...+{p_2}^{c_2})...({p_k}^{0}+{p_k}^{1}+{p_k}^{2}+...+{p_k}^{c_k})$
Sigma(大写Σ,小写σ),是第十八个希腊字母。
**举栗子**
> $180= 2^2 * 3^2 * 5$
> 约数和$=(1+2+4) * (1+3+9 ) * (1+5)=546$
#### 2、理解反素数
说白了,素数之所以称为素数,就是因为约数少,只有$1$和自己。
**反素数**,也就可以理解为 :约数多,而且每个数的约数个数,都比小于自己的数字约数个数多。
#### 3、反素数性质1
$1\sim N$中最大的 **反素数**,就是 **$1\sim N$中约数个数最多的数中最小的一个**。
* $g(x)$=约数个数,求最大的反素数,不就是在求哪个数的约数个数最多嘛~
* 如果不是最小的那一个,必然会出现`g(x)=g(i)`,与反素数的定义就矛盾了~
#### 4、反素数性质2
联想约数个数公式:
$d(N)=(c_1+1)\times (c_2+1)\times ... \times (c_k+1)$
一个数的约数个数,不与具体的质数因子相关,只与质数因子的**幂次**相关!
换言之,由于我们追求**最小**的数字,那么根据 贪心思想 ,我们知道:质因子越小,幂次越大,才可以保障最终的数字最小!!!
即$c_1>=c_2>=c_3>=...>=c_k$
**举个栗子**
> 假设$t=2^{c_1}×3^5×5^4×7^{c_4}…$
> 则$c_1>=5>=4>=c_4$,交换$3$和$5$的次数即变成$t=2^{c_1}×3^4×5^5×7^{c_4}…$,这样交换约数个数相同,可$3^4∗5^5 > 3^5∗5^4$,交换后该数变大了
#### 5、反素数性质3
* **$1\sim N$中任何数的不同质因子都不会超过$9$个**
因为$2 \times 3\times 5\times 7\times 11\times 13\times 17\times 19\times 23\times 29 >2\times 10^9$:
```cpp {.line-numbers}
#include
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
* **所有质因数的指数总和最大不超过$30$**
因为$2^{31}>2\times 10^9$.
根据上面一系列性质,我们得出了最简洁的思路,使用$dfs$,尝试确定前九个质数的指数,然后满足指数单调递减,总乘积不超过$N$,且同时记录约数的个数。
### 三、由小到大枚举
```cpp {.line-numbers}
#include
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`范围内,最大的反素数拥有的约数个数是$1600$个 ,这个是整数范围内约数个数的极限值, 需要记住这个值,后面的做题当中可以当做常数使用!
输入$2e9$,输出$1536$。
输入`INT_MAX`,即$2147483647$,输出$1600$。