1 条题解

  • 0
    @ 2025-7-29 15:48:16
    #include <bits/stdc++.h>
    using namespace std;
    
    /*
     * 题意回顾
     * 给定 x,考虑由 x 的因子(每个都 > 1)组成的序列 d1, d2, ..., dk,要求 d_i | d_{i+1}。
     * 问:最大长度 k,以及具有该最大长度的不同序列的个数。
     *
     * 重要结论(经典结论):
     * 设 x 的素因子分解为 x = p1^a1 * p2^a2 * ... * pm^am,
     * 记 L = a1 + a2 + ... + am(素因子个数的“重数和”,又记为 Ω(x))。
     * 1) 能达到的最大长度 = L。
     *    证明直观:把序列看作“从某个质因子开始,每一步把某个素因子指数 +1”,
     *    想要长度尽可能长,就必须一步只增加 1(即每次只乘上一个素因子),
     *    最多能增加到总指数和 L;且第一项必须是某个质因子(>1),否则少一步。
     * 2) 达到最大长度 L 的序列条数 = 将多重集合 {p1×(a1 个), p2×(a2 个), ...} 排序的种数,
     *    即多重集排列数:  L! / (a1! * a2! * ... * am!)
     *
     * 因此做法:
     * - 试除分解 x(x ≤ 2^20,最多 1e6 级,简单试除即可)。
     * - L 取指数和。
     * - 计数为多重排列数,可用阶乘比值:ans = fact[L] / Π fact[ai]。
     *   这里 L ≤ 20(因为 x ≤ 2^20),20! 在 64 位整数范围内,安全。
     */
    
    // 预计算 0..20 的阶乘(20! = 2,432,902,008,176,640,000 < 2^63-1)
    static unsigned long long fact[21];
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        // 预计算阶乘
        fact[0] = 1;
        for (int i = 1; i <= 20; ++i) {
            __int128 t = (__int128)fact[i - 1] * i;
            fact[i] = (unsigned long long)t; // 在范围内,安全
        }
    
        unsigned long long x;
        while ( (cin >> x) ) {
            // 特判 x = 1:没有 >1 的因子,只能是空序列,长度 0,方案数 1
            if (x == 1) {
                cout << 0 << ' ' << 1 << "\n";
                continue;
            }
    
            // 分解 x
            vector<int> exps;   // 存放各素因子指数 ai
            unsigned long long t = x;
    
            for (unsigned long long p = 2; p * p <= t; ++p) {
                if (t % p == 0) {
                    int cnt = 0;
                    while (t % p == 0) {
                        t /= p;
                        ++cnt;
                    }
                    exps.push_back(cnt);
                }
            }
            if (t > 1) exps.push_back(1); // 还剩一个大于 1 的素因子(指数为 1)
    
            // 最大长度 L = 所有指数之和
            int L = 0;
            for (int e : exps) L += e;
    
            // 方案数 = L! / (a1! * a2! * ... * am!)
            // 用 128 位中间变量确保中间除法安全(不过这里都能整除)
            __int128 ways128 = fact[L];
            for (int e : exps) {
                ways128 /= fact[e];
            }
            unsigned long long ways = (unsigned long long)ways128;
    
            cout << L << ' ' << ways << "\n";
        }
        return 0;
    }
    
    
    • 1

    信息

    ID
    1084
    时间
    1000ms
    内存
    64MiB
    难度
    10
    标签
    递交数
    1
    已通过
    0
    上传者