反素数
定义
- 素数的定义:素数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
- 反素数的定义:在一个区间内,因子最多的数(并且因子个数相同的时候值最小),所以反素数是相对于一个集合来说的。
特点
- 反素数肯定是从2开始的连续素数的幂次形式的乘积。
- 数值小的素数的幂次大于等于数值大的素数。即:
n
=
P
1
k
1
∗
P
2
k
2
∗
P
3
k
3
∗
⋅
⋅
⋅
∗
P
n
−
1
k
n
−
1
∗
P
n
k
n
n = {P_1}^{k_1}*{P_2}^{k_2}*{P_3}^{k_3}*···*{P_{n-1}}^{k_{n-1}}*{P_n}^{k_n}
n=P1k1∗P2k2∗P3k3∗⋅⋅⋅∗Pn−1kn−1∗Pnkn 中,有
P
1
≤
P
2
≤
P
3
≤
⋅
⋅
⋅
≤
P
n
−
1
≤
P
n
P_1 \le P_2 \le P_3 \le···\le P_{n-1} \le P_n
P1≤P2≤P3≤⋅⋅⋅≤Pn−1≤Pn 且
k
1
≤
k
2
≤
k
3
≤
⋅
⋅
⋅
≤
k
n
−
1
≤
k
n
k_1 \le k_2 \le k_3 \le···\le k_{n-1} \le k_n
k1≤k2≤k3≤⋅⋅⋅≤kn−1≤kn.
解释:1. 如果不是从2开始的素数,那么如果幂次不变,存在一个比2大的素数,那么因子个数不变的情况下,n的值会变大。反过来说就是一定从2开始的素数。
2.如果数值小的素数的幂次小于数值大的素数的幂次,那么交换这两个素数的幂次,因子数量不变,会使n变小。
注意
- 对于给定的n,要枚举到哪一个素数呢?
最极端的情况 : n = P 1 ∗ P 2 ∗ P 3 ∗ ⋅ ⋅ ⋅ ∗ P n − 1 ∗ P n n = P_1 * P_2*P_3 * ···*P_{n-1} * P_n n=P1∗P2∗P3∗⋅⋅⋅∗Pn−1∗Pn. 所以只要连续素数连乘到刚好小于等于n就可以了。再大了,连全都一次幂,都用不了,当然就是用不到的啦! - 我们要枚举到多少次幂呢?
极端的情况: n = 2 k 1 n = 2^{k_1} n=2k1 最大也就是 k 1 k_1 k1次,所以每个枚举 k 1 k_1 k1次即可。
实现
我们可以以每个素数作为一棵树的根节点,然后一层一层递归查找。找到什么时候停止呢?
- 当前走到的数字 c u r cur cur已经大于我们想要的数字 a n s ans ans了
- 当前因子 p r i m e [ i ] prime[i] prime[i]大于我们想要的因子 a n s ÷ c u r ans \div cur ans÷cur了
- 当前因子正好是我们想要的因子(此时判断是否需要更新最小ans)
例题链接:Codefoeces 27E
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int prime[15] = {2,3,5,7,11,13,17,19,23,29,31,37,41};
LL ans = 1e18;//题目说明最大值不超过1e18
int n;
void dfs(int i, LL cur, LL factor)
{
if(factor>n)return ;
if(factor==n&&cur<ans){ans = cur;}
for(int j = 1; j <= 61; j++)//1<<61大于等于1e18
{
if(ans<=cur*prime[i])return ;
dfs(i+1,cur*=prime[i],factor*(j+1));
}
}
int main()
{
scanf("%d", &n);
dfs(0,1,1);
printf("%lld\n", ans);
return 0;
}
最后贴个大佬的博客:https://zhuanlan.zhihu.com/p/41759808