大意
对于任何正整数 (x),其约数的个数记作 (g(x))。例如 (g(1)=1),(g(6)=4)。
如果某个正整数 (x) 满足:(0 lt i lt x),都有 (g(x) gt g(i)),则称 (x) 为反质数。例如,整数 (1,2,4,6) 等都是反质数。
现在给定一个数 (N),求出不超过 (N) 的最大的反质数。
题解
参考了黄东旭李煜东的蓝书,主要因为证明比较严谨(并不是在说老姚不严谨)。
- 引理1
1~(N) 中最大的反素数,就是 1~(N) 中约数最多的数中最小的那一个。
证明:
设 (x) 为 1~(N) 中约数最多的数中最小的数,那么有:
(forall i, 0<i<x),有 (g(i)<g(x)),说明 (x) 是一个反素数。
(forall i, x<i<N),有 (g(i) leq g(x)),说明大于 (x) 的都不是反素数。
故 (x) 即为所求。
-
引理2
1~(N) 中任何数的不同的质因子都不会超过10个:因为最小的11个素数的乘积:(2 imes 3 imes 5 imes 7 imes 11 imes 13 imes 17 imes 19 imes 23 imes 29 imes 31) 超过了 (2 imes 10^9)
1~(N) 中任何数的质因子的指数之和也不会超过30,因为即使是只有最小的质因子2,(2^{31}) 也超过了 (2 imes 10^9)。((2^{31}-1)是2147483647) -
引理3
(forall xin [1,N]),(x)为反素数的必要条件是 (x) 可分解成 (2^{c_1} imes 3^{c_2} imes 5^{c_3} imes ... imes 29^{c_{10}}),并且 (c_1 geq c_2 geq ... geq c_{10} geq 0),简单地说就是 (x) 的质因子都是连续的,并且指数不上升(其实也就是指数单调递减,之所以说不上升是考虑到存在后面几个质因子指数都为0的情况。)。
首先为什么质因子必须是由小到大连续的,因为反素数的定义是与质因子的个数有关的,如果一个数分解出的质因子不连续,我们完全可以用空掉的那个因子替换掉后面的某个更大的因子,从而得到一个更小的数,而质因子的个数并不变,故该数不为反素数。举个例子:(84=2^2 imes 3 imes 7),中间缺少了(5),所以存在一个数(60=2^2 imes 3 imes 5),60小于84,但约数的个数却与84相等,可以证明84不是一个反素数。
然后指数不上升也是同理,对于一个较大质因子的指数大于较小质因子的指数的数,在质因子个数不变的情况下,我们完全可以找到一个数,让它的小因子的指数较大而大因子的指数较小,从而使它的数值小于刚才的数,证得刚才的数并不是反素数。再举个例子:对于(540=2^2 imes 3^3 imes 5),存在(360=2^3 imes 3^2 imes 5),360小于540,但约数个数相等,说明540不是反素数。
蓝书上的对于该部分的说明可能不是很好理解,我觉得以上的解释可能帮助大家理解一下。
综上所述,思路就是用DFS枚举确定前10个质因子各自的指数(c_i)((c_i in [0,30])),并且满足指数单调递减、总乘积不超过(N),同时记录质因子个数。每找到一个符合条件的数就按照引理1的思路更新答案即可。
写完了...下面上代码...
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 50;
const int INF = 0x3f3f3f3f;
int prime[15]={0,2,3,5,7,11,13,17,19,23,29,31};
inline int read(){
int x = 0, w = 1;
char ch;
while(ch < '0' || ch > '9'){if(ch == '-')w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * w;
}
int n, mx, ans, s[maxn];
void Dfs(int x, int sum, int maxx){
//x表示当前枚举到第几个质因子
//sum表示存在几个约数
//maxx为当前的数值
if(x > 10) return;
if(sum > mx || (sum == mx && maxx < ans)){
mx = sum;
ans = maxx;
}
s[x] = 0;//s[x]记录x位质因子的指数
while(maxx * prime[x] <= n && s[x] < s[x - 1]){
s[x]++;
maxx = maxx * prime[x];
int w = sum * (s[x] + 1);
Dfs(x + 1, w, maxx);
}
}
signed main(){
n = read();
s[0] = 1000000;
Dfs(1, 1, 1);
printf("%lld
", ans);
return 0;
}