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 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 \times 23 \times 29 \times 31\) 超过了 \(2 \times 10^9\)
    1~\(N\) 中任何数的质因子的指数之和也不会超过30,因为即使是只有最小的质因子2,\(2^{31}\) 也超过了 \(2 \times 10^9\)。(\(2^{31}-1\)是2147483647)

  • 引理3
    \(\forall x\in [1,N]\)\(x\)为反素数的必要条件是 \(x\) 可分解成 \(2^{c_1} \times 3^{c_2} \times 5^{c_3} \times ... \times 29^{c_{10}}\),并且 \(c_1 \geq c_2 \geq ... \geq c_{10} \geq 0\),简单地说就是 \(x\) 的质因子都是连续的,并且指数不上升(其实也就是指数单调递减,之所以说不上升是考虑到存在后面几个质因子指数都为0的情况。)。

首先为什么质因子必须是由小到大连续的,因为反素数的定义是与质因子的个数有关的,如果一个数分解出的质因子不连续,我们完全可以用空掉的那个因子替换掉后面的某个更大的因子,从而得到一个更小的数,而质因子的个数并不变,故该数不为反素数。举个例子:\(84=2^2 \times 3 \times 7\),中间缺少了\(5\),所以存在一个数\(60=2^2 \times 3 \times 5\),60小于84,但约数的个数却与84相等,可以证明84不是一个反素数。

然后指数不上升也是同理,对于一个较大质因子的指数大于较小质因子的指数的数,在质因子个数不变的情况下,我们完全可以找到一个数,让它的小因子的指数较大而大因子的指数较小,从而使它的数值小于刚才的数,证得刚才的数并不是反素数。再举个例子:对于\(540=2^2 \times 3^3 \times 5\),存在\(360=2^3 \times 3^2 \times 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\n", ans);
	return 0;
}
posted @ 2020-08-02 15:36  zfio  阅读(92)  评论(0编辑  收藏  举报