洛谷题单指南-进阶数论-P2152 [SDOI2009] SuperGCD

原题链接:https://www.luogu.com.cn/problem/P2152

题意解读:求两个大数的最大公约数

解题思路:

大数不便于做除法取模,因此辗转相除法不合适。

但是高精度减法可行,考虑更相减损术!

1、更相减损术

如果a>=b,gcd(a,b) = gcd(a-b, b)

2、优化版

直接使用原始的更相减损术时间时间复杂度为O(n)

如果a、b都是偶数,那么gcd(a, b) = 2 * gcd(a / 2, b / 2)

如果a、b有一个是偶数,假设a是偶数,那么gcd(a, b) = gcd(a / 2, b)

由于当a、b有一个是偶数时,每次递归可以将数减半,而两个都是奇数时,a-b则一定是偶数,下次还是会减半

因此这样一来gcd整体递归的次数可以认为是O(logn)

100分代码:

#include <bits/stdc++.h>
using namespace std;

vector<int> a, b, result;

int cmp(vector<int> &a, vector<int> &b) // 比较两个大数的大小
{
    if(a.size() != b.size()) return a.size() > b.size() ? 1 : -1;
    for(int i = a.size() - 1; i >= 0; i--)
    {
        if(a[i] != b[i]) return a[i] > b[i] ? 1 : -1;
    }
    return 0; // 相等
}

vector<int> sub(vector<int> &a, vector<int> &b) // a - b
{
    result.clear();
    int t = 0; // 进位
    for(int i = 0; i < a.size(); i++)
    {
        t = a[i] - t;
        if(i < b.size()) t -= b[i];
        result.push_back((t + 10) % 10);
        if(t < 0) t = 1;
        else t = 0;
    }
    while(result.size() > 1 && result.back() == 0) result.pop_back();
    return result;
}

vector<int> div2(vector<int> &a) // 大数除以2
{
    result.clear();
    int t = 0; // 余数
    for(int i = a.size() - 1; i >= 0; i--)
    {
        t = t * 10 + a[i];
        result.push_back(t / 2);
        t %= 2;
    }
    reverse(result.begin(), result.end());
    while(result.size() > 1 && result.back() == 0) result.pop_back();
    return result;
}

vector<int> mul(vector<int> &a, int b) // 大数乘以一个int型整数
{
    result.clear();
    int t = 0; // 进位
    for(int i = 0; i < a.size(); i++)
    {
        t += a[i] * b;
        result.push_back(t % 10);
        t /= 10;
    }
    while(t)
    {
        result.push_back(t % 10);
        t /= 10;
    }
    return result;
}

vector<int> supergcd(vector<int> &a, vector<int> &b) 
{
    if(cmp(a, b) == 0) return a;
    if(a[0] % 2 == 0 && b[0] % 2 == 0) 
    {
        a = div2(a);
        b = div2(b);
        a = supergcd(a, b); // 复用a,减少空间
        return mul(a, 2);
    }
    else if(a[0] % 2 == 0 || b[0] % 2 == 0) 
    {
        if(a[0] % 2 == 0) a = div2(a);
        if(b[0] % 2 == 0) b = div2(b);
        return supergcd(a, b);
    }
    else
    {
        if(cmp(a, b) < 0) swap(a, b);
        a = sub(a, b); // 复用a,减少空间
        return supergcd(a, b);
    }
}

int main()
{
    string sa, sb;
    cin >> sa >> sb;
    for(int i = sa.size() - 1; i >= 0; i--) a.push_back(sa[i] - '0');
    for(int i = sb.size() - 1; i >= 0; i--) b.push_back(sb[i] - '0');
    vector<int> ans = supergcd(a, b);
    for(int i = ans.size() - 1; i >= 0; i--) cout << ans[i];
    return 0;
}

 

posted @ 2025-10-16 17:44  hackerchef  阅读(4)  评论(0)    收藏  举报