1 条题解

  • 0
    @ 2026-5-16 23:12:51

    1950D - 二进制小数的乘积 详细题解(官方标程版)

    一、题目翻译(规范格式,数字/公式全加$)

    题目名称

    D. 二进制小数的乘积

    题目描述

    如果一个正整数的十进制表示中只包含数字 0011,那么这个数被称为二进制十进制数。 例如:10101111010111 是二进制十进制数,而 1020110201787787 不是。

    给定一个数 nn,你需要判断 nn 是否可以表示为若干个(不一定互不相同)二进制十进制数的乘积

    输入格式

    • 第一行包含一个整数 tt1t51041 \le t \le 5 \cdot 10^4),表示测试用例数量。
    • 每个测试用例一行,包含一个整数 nn1n1051 \le n \le 10^5)。

    输出格式

    如果 nn 可以表示为二进制十进制数的乘积,输出 YES\texttt{YES};否则输出 NO\texttt{NO}


    二、标程核心思路

    1. 预处理

    首先预处理出所有不超过 10510^5 的二进制十进制数。 方法:遍历 11051 \sim 10^5 的所有数,判断每一位是否仅为 0011

    2. 合法判定规则(标程递归定义)

    我们称一个数是好数(合法),当且仅当:

    • n=1n = 1或者
    • 存在一个二进制十进制数 i>1i>1,使得 ini \mid n,且 ni\dfrac{n}{i} 是好数。

    3. 关键性质

    • 不超过 10510^5 的二进制十进制数不超过 3232,数量极少。
    • 因此直接暴力递归/循环除法即可通过,无需记忆化。

    三、标程解法公式

    1. 二进制十进制数判定

    对数字 xx,对 1010 取模,每一位只能是 0011

    ddigits(x), d{0,1}\forall d \in \text{digits}(x),\ d \in \{0,1\}

    2. 合法性判定

    $$\text{is\_good}(n) = \begin{cases} \text{true} & n=1 \\ \bigvee\limits_{\substack{i \in \text{bin-dec} \\ i>1,\ i \mid n}} \text{is\_good}\left(\dfrac{n}{i}\right) & \text{else} \end{cases} $$

    3. 复杂度上界(标程数学分析)

    递归复杂度上界:

    $$T(n) \leq 2T\left(\left\lfloor\frac{n}{10}\right\rfloor\right) + 4T\left(\left\lfloor\frac{n}{100}\right\rfloor\right) + 26T\left(\left\lfloor\frac{n}{1000}\right\rfloor\right) $$

    Akra-Bazzi 定理

    $$T(n) \in \mathcal{O}(n^{\log_{10}\alpha}) \approx \mathcal{O}(n^{0.635}) $$

    其中 α\alpha 是方程

    α3=2α2+4α+26\alpha^3 = 2\alpha^2 + 4\alpha + 26

    的唯一实根。

    更精确估计可得:

    T(n)O(n0.587)T(n) \in \mathcal{O}(n^{0.587})

    四、标程完整 AC 代码(可直接提交)

    #include <bits/stdc++.h>
    using namespace std;
    
    vector<int> bin;
    
    // 判断是否是二进制十进制数
    bool is_bin(int x) {
        while (x) {
            int d = x % 10;
            if (d != 0 && d != 1) return false;
            x /= 10;
        }
        return true;
    }
    
    // 预处理所有 <=1e5 的二进制十进制数
    void pre() {
        for (int i = 1; i <= 100000; ++i) {
            if (is_bin(i)) {
                bin.push_back(i);
            }
        }
    }
    
    // 判断 n 是否合法(标程暴力法)
    bool check(int n) {
        if (n == 1) return true;
        for (int x : bin) {
            if (x > n) break;
            if (n % x == 0 && x > 1) {
                if (check(n / x)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        pre();
        
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cout << (check(n) ? "YES" : "NO") << '\n';
        }
        return 0;
    }
    

    五、标程总结

    1. 预处理 105\le 10^5 的所有二进制十进制数(共约 3232 个)。
    2. 对每个 nn 暴力尝试用二进制十进制数反复除
    3. 能除到 11 输出 YES\texttt{YES},否则 NO\texttt{NO}
    4. 时间复杂度极优,可轻松处理 t=5104t=5\cdot 10^4

    需要我把所有合法答案打表生成,做成 O(1)\mathcal{O}(1) 极速版代码吗?

    • 1

    信息

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