Files
python/TangDou/AcWing_TiGao/T5/YueShuGeShu/198.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

169 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

##[$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})$
<font color='red'> Sigma(大写Σ,小写σ),是第十八个希腊字母。 </font>
**举栗子**
> $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)$
一个数的约数个数,不与具体的质数因子相关,只与质数因子的**幂次**相关!
换言之,由于我们追求**最小**的数字,那么根据 <font color='red'><b>贪心思想</b></font> ,我们知道:质因子越小,幂次越大,才可以保障最终的数字最小!!!
即$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 <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
* **所有质因数的指数总和最大不超过$30$**
因为$2^{31}>2\times 10^9$.
根据上面一系列性质,我们得出了最简洁的思路,使用$dfs$,尝试确定前九个质数的指数,然后满足指数单调递减,总乘积不超过$N$,且同时记录约数的个数。
### 三、由小到大枚举
```cpp {.line-numbers}
#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`范围内,<font color='red'><b>最大的反素数拥有的约数个数是$1600$个</b></font> ,这个是整数范围内约数个数的极限值, <font color='red'><b>需要记住这个值</b></font>,后面的做题当中可以当做常数使用!
输入$2e9$,输出$1536$。
输入`INT_MAX`,即$2147483647$,输出$1600$。