解题思路:
超级数什么的,其实就是反素数,这两者的定义是一模一样的,所以下面直接介绍反素数。
首先,大家应该都知道素因子分解(看到紫题还点进来的一般都会吧 Q w Q QwQ QwQ )。即: n u m = p 1 k 1 × p 2 k 2 × p 3 k 3 × . . . × p n k n num=p1^{k1}\times p2^{k2} \times p3^{k3}\times ... \times pn^{kn} num=p1k1×p2k2×p3k3×...×pnkn 其中 p 是素数, k 是指数。
那么考虑一般的求反素数方法,就是对于一个范围内数,对其进行素因子分解,然后与 ans 来比较。
显而易见,这种算法效率很低。究其本质,其实是有很多数的因数很少,这种算法却无法预知这种数,而不得不一点一点的排查。
由此,我们可以想到主动构造反素数。
首先给出一条结论:
所有的反素数在素因子分解后 k 数组是不上升的(p数组递增的情况下)。
这个可以用反证法证明:
假设有这样一个反素数 num 其素因子分解后有 k i < k j k_i < k_j ki<kj 且 $ p_i < p_j $ ,那么,如果将 k i k_i ki 和 k j k_j kj 这两者交换一下,很明显新的数要比原来的小,同时两者因数个数相等,都等于 ( k 1 + 1 ) × ( k 2 + 1 ) × ( k 3 + 1 ) × . . . × ( k n + 1 ) (k_1+1)\times (k_2+1)\times(k_3+1)\times ... \times(k_n+1) (k1+1)×(k2+1)×(k3+1)×...×(kn+1) ,而根据反素数的定义,反素数的因数个数是比其小的所有数中最大的(不包含等于),这与反素数的定义矛盾,所以 num 一定不是反素数。命题得证。
有这个结论可以很容易推出反素数一定是从最小素数2开始的连续素数的幂次的乘积。
所以说,我们可以枚举反素数在素因数分解后的素数个数,然后枚举每个素数的幂次,构造反出素数。(其实就是个 dfs )
代码如下:
#include<cstdio>
using namespace std;
int pri[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
//乘积等于614889782588491410恰好比1e17大一点
//乘上下一个素数53就是32589158477190044730,有点浪费了
long long ans_num,ans_tot,n,T;
void dfs(int now,long long num,long long tot,long long top){
// 当前素数 当前数值 当前因数数 能取到的最大i
if(num>n||now>=15)return;//15,数组从0开始
if(tot>ans_tot){ans_tot=tot;ans_num=num;}
if(tot==ans_tot&&ans_num>num){ans_num=num;}
//优先因数个数,其次保证最小
for(int i=1;i<=top;i++){
if(num*pri[now]>n)break;
num*=pri[now];
dfs(now+1,num,tot*(i+1),i);
}
}
int main(){
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
ans_num=0;ans_tot=0;
dfs(0,1,1,57);
//2^57=1.44115188075855872e+17,恰好比1e17大一点
printf("%lld\n",ans_num);
}
return 0;
}
然后就 TLE 了两个点。
重新读题,发现其实算反素数效率已经很高了,但架不住 m ≤ 1 0 5 m\leq 10^5 m≤105 的数据范围。
然后我们就想到可以把反素数全都算出来再应付询问。这里可以打表。也可以算出一个反素数后再算从一到比这个反素数小一的反素数。效率都挺高的,就是打表有点难看。
代码如下(稍微改改即可):
#include<algorithm>
#include<cstdio>
using namespace std;
long long max(long long a,long long b){if(a<b)return b;return a;}
int pri[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
//乘积等于614889782588491410恰好比1e17大一点
//乘上下一个素数53就是32589158477190044730,有点浪费了
long long ans_num,ans_tot,n,T,ask[100005],maxn,rec[100005],len;
void dfs(int now,long long num,long long tot,long long top){
// 当前素数 当前数值 当前因数数 能取到的最大i
if(num>n||now>=15)return;//15,数组从0开始
if(tot>ans_tot){ans_tot=tot;ans_num=num;}
if(tot==ans_tot&&ans_num>num){ans_num=num;}
//优先因数个数,其次保证最小
for(int i=1;i<=top;i++){
if(num*pri[now]>n)break;
num*=pri[now];
dfs(now+1,num,tot*(i+1),i);
}
}
int main(){
scanf("%lld",&T);
for(int i=1;i<=T;i++){
scanf("%lld",&ask[i]);
maxn=max(maxn,ask[i]);
}
n=maxn;
while(n!=1){
ans_num=0;ans_tot=0;
dfs(0,1,1,57);
len++;
rec[len]=ans_num;
n=ans_num-1;
}
sort(rec+1,rec+len+1);
for(int i=1;i<=T;i++){
int pos=upper_bound(rec+1,rec+len+1,ask[i])-rec-1;
printf("%lld\n",rec[pos]);
}
return 0;
}