題目描述
原題來自:POI 2001
如果一個大於等於 1 的正整數 n,滿足所有小於 n 且大於等於 1 的所有正整數的約數個數都小於 n 的約數個數,則 n 是一個反素數。譬如:1, 2, 4, 6, 12, 24,它們都是反素數。
請你計算不大於 n 的最大反素數。
輸入
一行一個正整數 n。
輸出
只包含一個整數,即不大於 n 的最大反素數。
樣例輸入
1000
樣例輸出
840
提示
數據範圍與提示
對於 10% 的數據,1≤n≤10^3;
對於 40% 的數據,1≤n≤10^6;
對於 100% 的數據,1≤n≤2×10^9。
來源/分類
題解:
這題對我這種數學蒟蒻來說真是太難了
首先,這題需要用到兩個定理:唯一分解定理以及因數個數定理
唯一分解定理:任何一個大於1的自然數 N,如果N不爲質數,那麼N可以唯一分解成有限個質數的乘積
這裏p1<p2<p3<...<pn,且均爲質數
因數個數定理:
因數個數定理可以套用上面的式子——
而N的因數個數就爲:
總結一下,就是指數+1連乘
從公式可以看出——只有指數影響因數個數!
在唯一分解定理中可以看出指數放置的位置對於因數個數來說並不影響
舉個肥腸簡單的栗子:與的因數個數相同
因爲所有小於n的數其因數個數都少於n,所以相同約數個數,數越小越好。
接下來可以運用一下貪心,得到一個策略:將盡量大的指數搭配在儘量小的質因數上
而數據範圍小於,所以指數最大31,直接dfs即可!
但是這道題還有一個坑點:那就是所有變量最好都開long long(否則就像我一樣,運行錯誤40%!)
代碼:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int prime[]={0,2,3,5,7,11,13,17,19,23,29,31};
ll n;
ll ans_num=0,ans_ges=0;
void dfs(ll now,ll ges,ll num,ll up)
{
if(ges>ans_ges)
{
ans_num=num;
ans_ges=ges;
}
else if(ges==ans_ges && num<ans_num) ans_num=num;
for(int i=1;i<=up;i++)
{
num*=prime[now];
if(num>n) return;
dfs(now+1,ges*(i+1),num,i);
}
}
int main()
{
scanf("%lld",&n);
dfs(1,1,1,31);
printf("%lld\n",ans_num);
return 0;
}