luoguP1463 [POI2002][HAOI2007]反素数

大意

对于任何正整数 (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;
}
原文地址:https://www.cnblogs.com/Zfio/p/13418925.html