AcWing 198. 反素数

AcWing 198. 反素数

一、题目描述

对于任何正整数 x,其约数的个数记作 g(x),例如 g(1)=1g(6)=4

如果某个正整数 x 满足:对于任意的小于 x 的正整数 i,都有 g(x)>g(i),则称 x 为反素数。

例如,整数 1246 等都是反素数。

现在给定一个数 N,请求出不超过 N 的最大的反素数。

输入格式
一个正整数 N

输出格式
一个整数,表示不超过 N 的最大反素数。

数据范围
1N2109

输入样例

1000

输出样例

840

二、解题思路

1、前置知识

N的唯一分解式:

N=P1c1P2c2...Pkck

知识总结

  • 约数个数公式
    d(N)=(c1+1)×(c2+1)×...×(ck+1)

举栗子

180=22325
约数个数=(1+2)(1+2)(1+1)=18

  • 约数和公式
    σ(n)=(p10+p11+p12+...+p1c1)(p20+p21+p22+...+p2c2)...(pk0+pk1+pk2+...+pkck)
    Sigma(大写Σ,小写σ),是第十八个希腊字母。

举栗子

180=22325
约数和=(1+2+4)(1+3+9)(1+5)=546

2、理解反素数

说白了,素数之所以称为素数,就是因为约数少,只有1和自己。
反素数,也就可以理解为 :约数多,而且每个数的约数个数,都比小于自己的数字约数个数多。

3、反素数性质1

1N中最大的 反素数,就是 1N中约数个数最多的数中最小的一个

  • g(x)=约数个数,求最大的反素数,不就是在求哪个数的约数个数最多嘛~
  • 如果不是最小的那一个,必然会出现g(x)=g(i),与反素数的定义就矛盾了~

4、反素数性质2

联想约数个数公式:
d(N)=(c1+1)×(c2+1)×...×(ck+1)

一个数的约数个数,不与具体的质数因子相关,只与质数因子的幂次相关!
换言之,由于我们追求最小的数字,那么根据 贪心思想 ,我们知道:质因子越小,幂次越大,才可以保障最终的数字最小!!!
c1>=c2>=c3>=...>=ck

举个栗子

假设t=2c1×35×54×7c4
c1>=5>=4>=c4,交换35的次数即变成t=2c1×34×55×7c4,这样交换约数个数相同,可3455>3554,交换后该数变大了

5、反素数性质3

  • 1N中任何数的不同质因子都不会超过9
    因为2×3×5×7×11×13×17×19×23×29>2×109:
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int main() {
    cout << INT_MAX << endl;
    LL s = 1;
    for (int i = 0; i <= 8; i++) s *= primes[i];
    cout << s << endl;
    s = 1;
    for (int i = 0; i <= 9; i++) s *= primes[i];
    cout << s << endl;
    return 0;
}

输出结果:

2147483647
223092870
6469693230

  • 所有质因数的指数总和最大不超过30
    因为231>2×109.

根据上面一系列性质,我们得出了最简洁的思路,使用dfs,尝试确定前九个质数的指数,然后满足指数单调递减,总乘积不超过N,且同时记录约数的个数。

三、由小到大枚举

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
const int N = 2e9;

int n;
int primes[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; // 枚举的质数前9位,足够了
int mc, mx;                                       // 最大反素数的约数个数,最大反素数值

/*
u:走到数组primes的第几位
last:前一个质数,我们取的指数是多少,当前位置取的质数的指数需要小于等于last
tmx: 当前路线上取得的最大反素数
tmc: 当前路线上取得的约数个数
*/
void dfs(int u, int last, int tmx, int tmc) {
    if (u == 9) { // 走完全程
        // ① 当前路径中约数个数大于最大约数个数,需要替换
        // ② 当前路径中的约数个数等于最大约数个数,但是,当前路径的数值更小,也需要替换
        if (tmc > mc || (tmc == mc && tmx < mx))
            mc = tmc, mx = tmx;
        return;
    }

    for (int i = 0; i <= last; i++) { // 幂次只能是小于等于前一个数的幂次
        int k = pow(primes[u], i);
        if (k * tmx > n) break; // 如果取当前这个素数的i次方,乘到原来的最大值后,大于n,那么此路径不通

        /*
        u+1:下一个素数位置
        i: 当前u这个素数取了i次幂
        tmx*k : 因为取了i次幂,所以,最大值变大了k倍
        tmc*(i+1) : 根据约数个数公式,因为有了质数 primes[u]后,取了i次幂,那么约数和增加(i+1)倍
        */
        dfs(u + 1, i, tmx * k, tmc * (i + 1));
    }
}

signed main() {
    cin >> n;
    dfs(0, 30, 1, 1); // 从最小的质数2出发,最大的幂次不超过30
    // n的最小取值是1,也就是小于等于1的范围内,最小的反素数是1,而且,约数的个数也是1
    cout << mx << endl;
}

四、经验总结

INT_MAX范围内,最大的反素数拥有的约数个数是1600 ,这个是整数范围内约数个数的极限值, 需要记住这个值,后面的做题当中可以当做常数使用!

输入2e9,输出1536
输入INT_MAX,即2147483647,输出1600

posted @   糖豆爸爸  阅读(125)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2015-05-20 TD的访问地址
2014-05-20 关于空间的改造方案
2013-05-20 现有的数据库与资源备份工作安排
Live2D
点击右上角即可分享
微信分享提示