P1574 超级数
Genius_Star · · 题解
题意简述:
额,裸的一道模板题目学过反素数的伙伴们,肯定非常眼熟,没错,这就是求我们不大于
思路:
网上搜索反素数表,开个数组存下来,然后二分查一以下……
上述事情是不可能发生的,就算打表,作为新时代的好少年们肯定也必须自己写代码来记录下来打表。
首先,先跟大家说一下反素数的定义(其实和题目没什么大区别):
-
对于任何正整数
x ,其约数的个数记作g(x) 。例如g(1)=1 、g(6)=4 。 -
如果某个正整数
x 满足:g(x)>g(i)(0<i<x) ,则称x 为反质数。例如,整数1,2,4,6 等都是反质数。
然后,说一下反素数的性质:
-
一个反素数的质因子必然是从
2 开始连续的质数. -
所以接下来我们需要求反素数!
求反素数:
1.暴力:
考虑直接暴力枚举以及的因数,时间复杂度约为
#include <cstdio>
int yz(int t) {
int ans=0;
for(int i=1; i<=t; i++) {
if(t%i==0) {
ans++;
}
}
return ans;
}
bool pd(int x) {
int dz=yz(x);
for(int i=1; i<=x-1; i++) {
if(yz(i)>=dz) {
return false;
}
}
return true;
}
int main() {
int n=0;
scanf("%d",&n);
for(int i=n; ; i--) {
if(pd(i)==true) {
printf("%d",i);
break;
}
}
return 0;
}
2.暴力优化:
还是考虑直接暴力枚举
#include <cstdio>
#include <cmath>
int yz(int t) {
int ans=0;
int mj=sqrt(t);
for(int i=1; i<=mj; i++) {
if(t%i==0) {
ans+=2;
}
}
if(mj*mj==t) {
ans--;
}
return ans;
}
bool pd(int x) {
int dz=yz(x);
for(int i=1; i<=x-1; i++) {
if(yz(i)>=dz) {
return false;
}
}
return true;
}
int main() {
int n=0;
scanf("%d",&n);
for(int i=n; ; i--) {
if(pd(i)==true) {
printf("%d",i);
break;
}
}
return 0;
}
3.满分思路:
什么是反素数?
反素数就是区间内约数个数最多的那个数。根据题目要求,如果有多个满足,选取最小的一个。
如果我们要求的区间就是
那么我们怎么求反素数呢?
如果我们设为
那么
如果设
所以当递增时是递减的,这个数才可能是反素数。
所以我们可以据此搜索。
素数表可以筛一波,也可以这样:
因为前
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long p[20]= {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
long long maxn=-1,ans=-1;
long long n=0;
void get(long long m,long long f,long long t,long long pr) {
//f为当前质数的编号,当前指数<pr
//t为当前约数的个数, m表示当前可能成为最优解的数
if(t>maxn || (t==maxn && m<ans)) { //更新最优解
ans=m,maxn=t;
}
long long i=m,j=0;
long long nt=0;
while(j<pr) { //j表示的是当前正在搜索的指数
j++;
if(n/i<p[f]) { //若不满足条件就跳出循环(i表示的是当前的m)
break;
}
nt=t*(j+1),i*=p[f];//更新新数以及它的因子个数。
if(i<=n) { //若i(即当前的m)在区间[1,n]内就继续搜索。
get(i,f+1,nt,j);
}
}
}
int main() {
scanf("%lld",&n);
get(1,1,1,30);
printf("%lld",ans);
return 0;
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int p[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47}; //打表
int n, tid[maxn];
ll now, res, val, Q[maxn], ans[maxn];
void dfs(int pos, ll sum, ll tot, int lst){ //搜索找反素数
for (ll i = 0, t = sum; i <= lst; i++, t *= p[pos]){
if (pos < 16) //前16个素数还没有搜完
dfs(pos + 1, t, tot * (i + 1), i);
if ((res >= t && val <= tot) || (res < t && val < tot))
res = t, val = tot;
if (t * p[pos] > now)
break;
}
}
bool cmp(int x,int y){
return Q[x]>Q[y];
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", Q + i), tid[i] = i;
sort(tid + 1, tid + n + 1,cmp);
for (int i = 1; i <= n; i++){
if (i > 1 && ans[tid[i - 1]] < Q[tid[i]])
ans[tid[i]] = ans[tid[i - 1]];
else{
res = val = 1;
now = Q[tid[i]];
dfs(1, 1, 1, 1000);
ans[tid[i]] = res;
}
}
for (int i = 1; i <= n; i++)
printf("%lld\n", ans[i]);
return 0;
}