洛谷:P1045:麦森数


洛谷:P1045:麦森数

题目

P1045:麦森数

分析

这题牵涉的数学知识不多,只有一个快速计算位数。对于\(2^P-1\)这样的数字的位数,可以用\(P\times log_{10}2+1\)快速计算——这就解决了第一问。

至于第二问,看到“500位数字”这样的要求,就知道一定要用高精度算法。注意,这里的高精度计算,涉及两部分:乘法,以及减法(最后计算-1)。

高精度乘法

乘法运算有两个核心运算:卷积以及进位。

我们手动计算乘积的时候,一般是从低到高(个位开始)在乘数中取一位,依次和被乘数的各位相乘,写下部分得数。然后将这些部分得数相加。

在用计算机模拟这个运算的时候,可以用更快一点的方式,也就是卷积。

假定有两个数:

\(A = a_0 + a_1\times 10^1 + a_2\times 10^2 + ...\)\(B = b_0 + b_1\times 10^1 + b_2\times 10^2 + ...\),那么它们的乘积: \(A × B = \sum_i\sum_j(a_i \times b_j \times 10^{i+j})\)。简单地说,就是:第i位和第j位相乘,得数是i*j,而“位数”是i+j。再直白一点说,就是5(00)*9(00),得数是45,而位数是4,也就是450000。这样的好处是,我们可以一次性计算出某一个位上的得数。也就是,对于结果result[i](第i位的得数),它一定是由a[j]*b[i-j]的总和得来。

至于进位,可以统一处理。

vector<int> multiply(const vector<int>& a, const vector<int>& b)
{
    vector<int> result(MAX_DIGITS, 0);

    for (int i = 0; i < MAX_DIGITS; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            result[i] += a[j] * b[i - j];
        }
    }

    // 处理进位
    int carry = 0;
    for (int i = 0; i < MAX_DIGITS; i++)
    {
        result[i] += carry;
        carry = result[i] / 10;
        result[i] %= 10;
    }

    return result;
}

快速幂

在计算快速幂的时候,我们会多次计算乘法,基本算法在之前已经有描述(见P1226:快速幂(模板)),只是在此处我们要用到高精度乘法而已。这里不再单列。

答案

Solution

思考

这里的乘法运算用到了“卷积”的方式,而不是传统的按位计算,是一个改进。

Previous Next