Label
素数及因子相关性质+贪心
Description
设 ∀ x ∈ N + \forall x\in N^+ ∀x∈N+,定义 g ( x ) = ∑ i = 1 x [ i ∣ x ] g(x)=\sum_{i=1}^{x}[i|x] g(x)=∑i=1x[i∣x](即 x x x的约数个数),若某个正整数 x x x满足: ∀ 0 < i < x , g ( x ) > g ( i ) \forall 0<i<x,g(x)>g(i) ∀0<i<x,g(x)>g(i),则称 x x x为反质数。现给定一数 N ( 1 ≤ n ≤ 2 × 1 0 9 ) N(1\leq n \leq 2\times 10^9) N(1≤n≤2×109),请求出不超过 N N N的最大的反质数。
Solution
首先,根据素数的唯一分解定理,若将任意合数 x x x改写为 ∏ p i c i \prod p_i^{c_i} ∏pici,那么根据乘法原理,易得其因子数为 ∏ ( c i + 1 ) \prod(c_i+1) ∏(ci+1)。
然后,我们考虑答案的构成:我们将任意正整数 x x x分解为 ∏ p i c i \prod p_i^{c_i} ∏pici,那么为了让 x x x的因子个数尽量多,我们应该避免“ p i p_i pi是较大的素数”这一情况的出现。进而,我们可以引出一个引理:
引理1:在此题数据范围下,答案一定可以表示成不超过9个最小的素数的乘积。
由于:
2 × 3 × 5 × 7 × 11 × 13 × 17 × 19 × 23 = 223 , 092 , 870 < 2 × 1 0 9 2\times3\times5\times7\times11\times13\times17\times19\times23=223,092,870<2\times 10^9 2×3×5×7×11×13×17×19×23=223,092,870<2×109
2 × 3 × 5 × 7 × 11 × 13 × 17 × 19 × 23 × 29 = 6 , 469 , 693 , 230 > 2 × 1 0 9 2\times3\times5\times7\times11\times13\times17\times19\times23\times 29=6,469,693,230>2\times 10^9 2×3×5×7×11×13×17×19×23×29=6,469,693,230>2×109
所以,若出现其它 ≥ 29 \geq29 ≥29的素因子,其一定不如换做更小因子的答案更优。(相同因子个数的数的集合:最小的数才有可能是答案,详见最后)。
引理2: ∑ c i ≤ 30 \sum c_i\leq30 ∑ci≤30
证明:由于 2 30 < 2 × 1 0 9 , 2 31 > 1 0 9 2^{30}<2\times 10^9,2^{31}>10^{9} 230<2×109,231>109,故 ∑ c i ≤ 30 \sum c_i\leq30 ∑ci≤30
引理3: c 1 ≥ c 2 ≥ c 3 ≥ . . . ≥ c i c_1\geq c_2\geq c_3\geq ...\geq c_i c1≥c2≥c3≥...≥ci
从贪心的角度出发,我们为了让各素数幂次积得到的数小一些,会让较小的素因子的次数大一些,故引理三肯定比其它情况优。
证明的话就是排序不等式的套路了,若存在微扰项 ( c i < c i + k ) (c_i<c_{i+k}) (ci<ci+k),那么将 c i , c i + k c_i,c_{i+k} ci,ci+k交换后得到的数 x x x肯定比原来的 x x x小。
最后注意:对于约数相同的一组数,只有其中最小的一个数才有可能是答案(其余数均不满足反素数定义)
Summary
1、素数的唯一分解定理经常作为相关题目的切入角度,即经常把数字转化为素数之积来思考问题;
2、充分考虑特殊情况;
3、此题的相关结论比较经典,不失为不错的思考角度。
Code
#include<cstdio>
#include<iostream>
#define ri register int
#define ll long long
using namespace std;
int prime[11]={0,2,3,5,7,11,13,17,19,23,29};
int fstpower,cnt[11];
ll bits=1,n,maxn,ans,powprime[11][35];
void dfs(ll now,int power,int step)
{
if(step==11) return;
for(ri i=cnt[step-1];i>0;--i)
{
if(now*powprime[step][i]>n||powprime[step][i]==0) continue;
if(power*(i+1)>maxn||(power*(i+1)==maxn&&now*powprime[step][i]<ans&&now*powprime[step][i]>0))
//now*powprime[step][i]<ans:对于约数相同的一组数,
//只有其中最小的一个数才有可能是答案(其余数均不满足反素数定义)
{
maxn=power*(i+1);
ans=now*powprime[step][i];
}
cnt[step]=i;
if(step<10&&now*powprime[step][i]*prime[step+1]<=n)
dfs(now*powprime[step][i],power*(i+1),step+1);
}
}
void fst()
{
for(ri i=1;i<=n;++i)
{
if(bits*2>n) { fstpower=i-1; break; }
bits*=2;
}
for(ri i=1;i<=10;++i)
{
powprime[i][0]=1;
for(ri k=1;k<=fstpower;++k)
{
powprime[i][k]=powprime[i][k-1]*prime[i];
if(powprime[i][k]>=n) break;
}
}
}
int main()
{
scanf("%lld",&n);
fst();
cnt[0]=fstpower;
dfs(1,1,1);
cout<<ans;
return 0;
}