题意:
联系之前学过的 欧拉函数 φ(x)
,我们将函数 φ(x)
变一下形,令 H(x) = φ(x) / x
,
之后题目要求输入 n
,并在 区间 [2, n]
中找到 令 H(x)
取最小值的 横坐标 x0
(若 有多个最小值 则 取最小的 x0
),
在 区间 [2, n]
中找到 令 H(x)
取最大值 的 横坐标 x0
(若 有多个最大值 则 取最大的 x0
)。
思路:
先说 本题答案的结论 是:
问题一的答案 为 2、2 × 3、2 × 3 × 5、2 × 3 × 5 × 7.….
这些前若干个素数的积中,最大的且不超过 n
的那一个,如 n = 233
,则答案为 2 × 3 × 5 × 7 = 210
。
问题二的答案 为 [2, n]
中最大的素数。
如果 n
为 1
,直接输出 -1
实现细节:
对问题一,因为 很少 的一些 素数前缀积 就会超过 10 ^ 9
了因此 预处理出前几十个素数即可,小心溢出。
对问题二,从 n
开始 依次递减地暴力判断 是否为 素数 即可,这是因为 10 ^ 9
以内最大的两个素数间隔是 282
,所以这样做 最多只要判断 282
个数字,且判断时往往 达不到根号的复杂度。
问题一:由于
之中 pi
取遍 x
的 所有种类的质因子(注意是 种类 而 不是个数,如:素因子 2
出现两次 在公式里也只 乘上一次),则
显然 乘的项数越多、每一项越小, H(x)
就越小,按照 2、3、5、7、11.....
的顺序取 pi
就可以 达到这一目标。
问题二:对于 素数 p
有
因此
直觉可以感到这是挺大的数(毕竟 H(x)
最大不超过 1
),猜测取 最大的素数 p
即可。
这种题适合于 打表,所以我们也可以通过 打表 的方式来观察输出的结果,猜测它们 满足什么样的规律。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int primes[N];
int cnt;
int n;
typedef vector<int> vi;
#define pb push_back
#define pp pop_back
vi pre_pro;
bool jud(int x) //判断是否为素数
{
if(x<2) return false;
for(int i=2; i<=x/i; ++i)
{
if(x%i==0) return false;
}
return true;
}
void init() //预处理1~N中的所有素数
{
for(int i=2; i<N; ++i)
{
if(jud(i))
{
primes[cnt++] = i;
}
}
}
void prefix_pro() //预处理素数前缀积
{
pre_pro.pb(1);
for(int i=1; i<=cnt; ++i)
{
int t = pre_pro[i-1] * primes[i-1];
pre_pro.pb(t);
if(t>9699690) break;
}
}
int main()
{
init();
prefix_pro();
int t; cin>>t;
while(t--)
{
int n; cin>>n;
if(n==1) puts("-1");
else
{
int mnpos;
for(int i=1; i<pre_pro.size(); ++i)
{
int v = pre_pro[i];
if(v<=n) mnpos = v;
}
cout<<mnpos<<' ';
int mxpos;
for(int i=n; i>=2; --i)
{
if(jud(i))
{
mxpos = i;
break;
}
}
cout<<mxpos<<'\n';
}
}
return 0;
}