1 条题解

  • 0
    @ 2026-10-26 3:53:48

    思路与算法

    在所有 v≤nv\le n 的整数中,先最大化约数个数,再在并列时取最小 vv。这个最小数的约数数严格超过它前面的每个数,故它是反素数;之后直到 nn 不可能再刷新纪录,因此它就是不超过 nn 的最大反素数。

    若 v=∏pieiv=\prod p_i^{e_i},约数数为 ∏(ei+1)\prod(e_i+1)。取得某个约数数的最小整数,使用的素数一定从2连续开始,否则替换成更小的缺失素数会减小整数而不改约数数。指数必须非增:小素数指数比大素数小,交换两指数会使整数更小,约数数仍相同。

    据此按素数 2,3,5,7,11,13,17,19,232,3,5,7,11,13,17,19,23 深搜,状态记录素数位置、允许的最大指数、当前数值和约数个数。先用当前状态更新最优答案,再尝试当前素数的正指数,下一层指数上限设为本层指数。若再乘一次会超过 nn,则终止本层后续尝试。前10个素数乘积 6469693230>2×1096469693230>2\times10^9,所以合法反素数至多9种不同素因子。首层指数至多30。

    正确性说明

    选择最大约数数并在并列时取最小整数的规则给出一个纪录数;若它之后还存在不超过 nn 的纪录数,后者约数数必须更大,与最大性矛盾。所以该规则与目标等价。

    缺失素数替换及指数交换证明表明,某个全局最优解一定由连续素数、非增正指数组成,深搜不会因这些限制遗漏最优解。每层枚举所有不超过上一层的指数;对当前数值先更新答案,也覆盖使用更少素数的解,包括1。若当前数值乘素数已超过上界,更高指数及任何继续扩展都不可能合法,因此剪枝安全。所有合法标准形都被遍历,维护的并列最小规则最终选出所求反素数。

    复杂度

    设实际访问的合法前缀状态数为 TT。每条递归边只生成一次,每层结束最多再做一次失败检查,总时间 O(T)O(T);一个宽松界为 O((1+⌊log⁡2n⌋)9)O((1+\lfloor\log_2 n\rfloor)^9),非增指数限制使实际状态远少于此界。质数表固定,初始化为常数;递归至多9层,额外空间在本题范围为 O(9)O(9)。不分配长度为 nn 的数组。

    易错点

    1. 并列约数数时必须取较小数;较大的并列数没有严格刷新纪录。
    2. 1本身是反素数,初始最优值和约数数均为1。
    3. 非增指数限制依赖最小整数证明,不能跳过小素数后任意继续搜索。
    4. 先用 value > n/p 判断再乘,避免溢出。递归参数传值,不让不同分支的数值和约数数互相污染。
    5. 数据包含纪录刚好到达及前一个数、最小值和最大 2×1092\times10^9。独立答案枚举全部164706个9素数平滑数,不使用非增指数剪枝,并用1至50000的全量约数筛核对。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    ll limit, best = 1;
    int bestCount = 1;
    void dfsAntiprime(int at, int maxExponent, ll value, int count) {
        if (count > bestCount || (count == bestCount && value < best)) {
            bestCount = count;
            best = value;
        }
        if (at == 9)
            return;
        ll next = value;
        for (int exponent = 1; exponent <= maxExponent; exponent++) {
            if (next > limit / primes[at])
                break;
            next *= primes[at];
            dfsAntiprime(at + 1, exponent, next, count * (exponent + 1));
        }
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cin >> limit;
        dfsAntiprime(0, 30, 1, 1);
        cout << best << '\n';
    }
    
    • 1

    信息

    ID
    1656
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者