[总结] 反素数

素数的定义可以为:因子只有2个的数(一个是1,另一个是本身),也就是因子最少的数。

那么,反素数可以定义成因子数最多的数,假如因子个数相同那么是最小的那个数,反素数是相对于一个集合来说的(比如在n个数以内的数)。

简而言之,因素最多并且值最小的数,就是反素数

朴素求法:利用整数唯一分解定理若x=pk11pk22pknn,那么x就有(k1+1)(k2+1)(kn+1)个因数。给某个集合扫一遍求下max即可。

反素数有两个特点:

  1. 反素数肯定是从2开始的连续素数的幂次形式的乘积。
  2. 数值小的素数的幂次大于等于数值大的素数:x=pk11pk22pknn,那么有k1k2kn

解释:

  1. 反证,若存在不从2开始的连续素数之积p,那么必然存在一个从2开始的连续素数之积q,除了一个2以外,其他值均与p相等,而此时q<p
  2. 若存在ki,kj,使得i<j,ki>kj成立。那么我们可以交换一下pipj,使得这个数更小。

想要求给定自然数n,求[1,n]内的反素数。这个问题似乎没有什么更加牛X的解决方法,实际上还是要通过枚举的方法,为了解决这个问题,我们再提出两个问题:

  1. 对于给定的n,要枚举到哪一个素数:枚举到n的素数就可以。
  2. 枚举多少次幂:实际上枚举到最小素数pk次幂,使得pk>n,那么枚举到的最大幂次肯定要小于k

一般这类问题都是DFS+一些分支限界枚举答案的。


练习1:http://codeforces.com/contest/27/problem/E

题意:找含有n个因子的最小的那个数,这个数不超过1E18。

分析:

  1. 设这个数字是m,那么可以设m=pk11pk22pkrr,其中(k1+1)(k2+1)(kr+1)=n。我们由上述结论1可以求出来pi的上界:取k1=2,那么最多也就是log21000=10个不同的素数,贪心取最小的前10个素数就可以。分析到这里就可以打表了。
  2. 再考虑这个数在1E18内的问题,也就是k的取值范围。取最小的p=2,那么顶多是60层。

于是我们就可以DFS,在每层(即每个素数)暴力枚举这个数选取次数,然后维护上一层的数值以及因数个数就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;

using ull = unsigned long long;
const int maxn = 111;
int n;
int p[maxn] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ull ret;

void dfs(int depth, ull cur, int cnt, int hi) {
if(depth >= 11 || cnt > n) return;
if(cnt == n) {
ret = min(ret, cur);
return;
}
for(int i = 1; i <= hi; i++) {
if(cnt * (i + 1) > n) break;
cur *= (ull)p[depth];
dfs(depth+1, cur, cnt*(i+1), i);
}
}

signed main() {
// freopen("in", "r", stdin);
while(~scanf("%d",&n)) {
ret = 1E18;
dfs(1, 1, 1, 60);
cout << ret << endl;
}
return 0;
}
Code 401: 未经授权的操作,请检查你的AppId和AppKey.
Powered By Valine
v1.5.2