P1463 [POI2002][HAOI2007]反素数 题解

博客园同步

原题链接

简要题意:

f x f_x fx 表示 x x x 的因数个数。求 ≤ n \leq n n 的最大的 x x x 使得 f x > f y ( 1 ≤ y < x ) f_x > f_y (1 \leq y < x) fx>fy(1y<x).(即求一个最大的比它小的数因数都多的数)

本题将作为反素数的模板题。

其实这就是 反素数 的定义,求反素数。

算法一

注意到 f x f_x fx 是积性函数。所以我们用欧拉筛筛出来。

然后再暴力判断反素数。

时间复杂度: O ( n ) O(n) O(n).

实际得分: O ( rp ) O(\texttt{rp}) O(rp).(看运气,因为这题没说部分分)

算法二

我们只需要求最大的反素数,不用求所有的。

比方说一个反素数 n = ∏ i = 1 k p i k i n = \prod_{i=1}^k {p_i}^{k_i} n=i=1kpiki 为其标准分解形式,其中 p i p_i pi 严格递增。

n n n 的因数个数为 ∏ i = 1 k ( k i + 1 ) \prod_{i=1}^k (k_i + 1) i=1k(ki+1)

此时必然存在: k i k_i ki 不严格递减。

为什么呢?

比方说 12 = 2 2 × 3 12 = 2^2 \times 3 12=22×3 18 = 2 × 3 2 18 = 2 \times 3^2 18=2×32.

此时两者因数个数相同,所以 18 18 18 肯定不是反素数。

也就是说,大的素数个数一定不能超过小的,这样就可以是反素数了。

那么你会问了,这怎么枚举呢?

1 1 1 开始轮流乘上素数(注意剪枝),具体见代码。

因为 2 × 3 × 5 × 7 × 11 × 13 × 17 × 19 × 23 × 27 > 2 × 1 0 9 2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 \times 23 \times 27 > 2 \times 10^9 2×3×5×7×11×13×17×19×23×27>2×109,所以只要这些素数就够了。

时间复杂度: O ( wys ) O(\text{wys}) O(wys).(难以分析,但是应该居于线性和对数之间,和开方差不多但难以证明)

实际得分: 100 p t s 100pts 100pts.

反素数算法太玄学

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n;
int p[20]={0,2,3,5,7,11,13,17,19,23,29};
ll ans=-1,maxn=-1;

inline void dfs(ll dep,int prime,int now,int zhi) {
//dep 是当前的数,prime 是当前可以选择的质数编号,now 是因数个数,zhi 是最大幂次
	if(now>maxn || (now==maxn && dep<ans)) ans=dep,maxn=now; //更新答案
	int j=0; ll i=dep; while(j<zhi) { //枚举幂次
		j++; if(n/i<p[prime]) break; //超出 n
		i*=p[prime]; if(i<=n) 
		dfs(i,prime+1,now*(j+1),j); //往后枚举
	}
}

int main(){
	n=read();
	dfs(1,1,1,30);
	printf("%lld\n",ans); //答案
	return 0;
}

### P1463 POI2001 HAOI2007 素数 C语言实现 #### 定义与性质 对于给定的数据范围 \( 1 \leq N \leq 2 \times 10^9 \),素数是指某个正整数 \( x \) 满足条件:对于所有的 \( 0 < i < x \),都有 \( g(x) > g(i) \)[^2]。这里 \( g(n) \) 表示 \( n \) 的约数个数。 #### 解决方案概述 为了高效求解此问题,可以采用预处理加深度优先搜索 (DFS) 或广度优先搜索 (BFS) 来枚举可能的结果,并通过剪枝优化来减少不必要的计算量[^1]。 #### 关键算法思路 核心在于利用质因数分解特性以及贪心策略,在遍历过程中尽可能早地排除不可能成为最优解的情况: - 使用 DFS 枚举所有小于等于最大允许因子数量的组合; - 对于每一个新的乘积项,检查其是否超出界限并更新当前最佳答案; - 剪枝原则包括但不限于:当已知更优解存在时不继续深入探索;控制各位置上的指数上限以防止重复计数等。 #### C语言代码实现 以下是基于上述原理的一个简化版C语言程序框架: ```c #include <stdio.h> #define MAX_PRIME 10 // 考虑前几个最小的质数即可覆盖大部分情况 int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // 预定义的小质数表 long long best_ans; void dfs(int pos, int limit, long long product, int divisor_count){ if(pos >= MAX_PRIME || product * primes[pos] > best_ans){return;} for(int exp=1;exp<=limit && product*primes[pos]<=best_ans;exp++){ product *= primes[pos]; if(product>best_ans&&divisor_count*(exp+1)>max_divisors){ max_divisors=divisor_count*(exp+1); best_ans=product; } dfs(pos+1, exp, product, divisor_count*(exp+1)); } } // 主函数部分省略... ``` 该片段展示了如何构建一个递归过程来进行有效搜索,并适时调整全局变量 `best_ans` 和 `max_divisors` 记录目前发现的最佳候选者及其对应的除数数目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值