1 条题解

  • 0
    @ 2025-7-29 15:41:21

    60分方法:

    #include <bits/stdc++.h>
    using namespace std;
    
    /* 题意:求两个超大整数(位数可达 3000)的最大公约数。
     * 做法:使用“欧几里得算法(辗转相除)”——但需要自己实现“大整数取模”。
     *
     * 关键点:
     * 1) 将 A、B 作为十进制字符串读入;
     * 2) 实现 string 版本的:去前导零、比较、减法、与“单个数字相乘”;
     * 3) 用“按位长除法”实现 (a % b):依次把被除数的每一位“放下”,
     *    对当前余数 rem 与除数 b,二分查找一位商 q∈[0..9],使 b*q ≤ rem < b*(q+1),
     *    然后 rem -= b*q。最终的 rem 即为 a%b。
     * 4) 在欧几里得算法中反复做取模,直到余数为 0。
     *
     * 复杂度:设位数为 N,单次取模为 O(N^2) 左右;本题只有两数,足够通过。
     */
    
    /* ---------- 工具函数:字符串大整数基础运算 ---------- */
    
    // 去掉前导零,保证“0”表示为单个字符 '0'
    static inline string trim_leading_zeros(const string &s) {
        size_t p = 0;
        while (p < s.size() && s[p] == '0') ++p;
        if (p == s.size()) return "0";
        return s.substr(p);
    }
    
    // 比较两个“无前导零”的非负整数字符串:返回 -1/0/1
    static inline int cmp_nonneg(const string &a, const string &b) {
        if (a.size() != b.size()) return (a.size() < b.size()) ? -1 : 1;
        if (a == b) return 0;
        return (a < b) ? -1 : 1; // 字典序比较在等长时等价于数值比较
    }
    
    // 计算 a - b,要求 a >= b,均为“无前导零”的非负整数字符串
    static string sub_nonneg(string a, const string &b) {
        // 从末位开始逐位相减
        int i = (int)a.size() - 1;
        int j = (int)b.size() - 1;
        int borrow = 0;
        while (j >= 0 || borrow) {
            int x = a[i] - '0' - borrow;
            int y = (j >= 0 ? b[j] - '0' : 0);
            if (x < y) { x += 10; borrow = 1; } else borrow = 0;
            a[i] = char('0' + (x - y));
            --i; --j;
        }
        // 前面剩余位不变,但可能产生前导零
        return trim_leading_zeros(a);
    }
    
    // 计算 a * d,其中 0 <= d <= 9,a 为“无前导零”的非负整数字符串
    static string mul_digit(const string &a, int d) {
        if (d == 0 || a == "0") return "0";
        int carry = 0;
        string res;
        res.resize(a.size());
        for (int i = (int)a.size() - 1; i >= 0; --i) {
            int t = (a[i] - '0') * d + carry;
            res[i] = char('0' + (t % 10));
            carry = t / 10;
        }
        if (carry) res.insert(res.begin(), char('0' + carry));
        return res;
    }
    
    /* ---------- 大整数取模:a % b(均为十进制非负整数字符串,b != "0") ---------- */
    static string big_mod(string a, string b) {
        a = trim_leading_zeros(a);
        b = trim_leading_zeros(b);
        // 若 a < b,直接返回 a
        if (cmp_nonneg(a, b) < 0) return a;
        // b == 1 则直接 0
        if (b == "1") return "0";
    
        string rem = "0"; // 当前余数
        rem.reserve(b.size() + 1);
    
        for (char ch : a) {
            // rem = rem * 10 + (ch - '0')
            if (rem == "0") rem = string(1, ch);  // 避免形成前导零
            else rem.push_back(ch);
    
            // 在 0..9 中二分出最大 q,使得 b*q <= rem
            int low = 0, high = 9, q = 0;
            while (low <= high) {
                int mid = (low + high) >> 1;
                string prod = mul_digit(b, mid);
                int r = cmp_nonneg(prod, rem);
                if (r <= 0) { q = mid; low = mid + 1; }
                else high = mid - 1;
            }
            if (q > 0) {
                string prod = mul_digit(b, q);
                rem = sub_nonneg(rem, prod);
            }
            // 若 q==0,rem 不变(仍然 < b),继续“放下”下一位
        }
        return trim_leading_zeros(rem);
    }
    
    /* ---------- 主流程:大整数 GCD(欧几里得算法) ---------- */
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        string A, B;
        if (!(cin >> A)) return 0;
        cin >> B;
    
        A = trim_leading_zeros(A);
        B = trim_leading_zeros(B);
    
        // 欧几里得算法:gcd(a, b) = gcd(b, a % b),直到 b == 0
        while (B != "0") {
            string R = big_mod(A, B);
            A = B;
            B = trim_leading_zeros(R);
        }
        cout << A << '\n';
        return 0;
    }
    
    
    • 1

    信息

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