题解:洛谷 P1045 麦森数

【题目来源】

洛谷:P1045 [NOIP 2003 普及组] 麦森数 - 洛谷(luogu.com.cn)

【题目描述】

形如 \(2^P-1\) 的素数称为麦森数,这时 \(P\) 一定也是个素数。但反过来不一定,即如果 \(P\) 是个素数,\(2^P-1\) 不一定也是素数。到 \(1998\) 年底,人们已找到了 \(37\) 个麦森数。最大的一个是 \(P=3021377\),它有 $909526 $ 位。麦森数有许多重要应用,它与完全数密切相关。

任务:\(P(1000\lt P\lt 3100000)\),计算 \(2^P-1\) 的位数和最后 \(500\) 位数字(用十进制高精度数表示)

【输入】

文件中只包含一个整数 \(P(1000\lt P\lt 3100000)\)

【输出】

第一行:十进制高精度数 \(2^P-1\) 的位数。

\(2∼11\) 行:十进制高精度数 \(2^P-1\) 的最后 \(500\) 位数字。(每行输出 \(50\) 位,共输出 \(10\) 行,不足 500 位时高位补 0)

不必验证 \(2^P-1\)\(P\) 是否为素数。

【输入样例】

1279

【输出样例】

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

【核心思想】

  1. 问题分析:计算 \(2^P - 1\)位数最后 500 位数字,其中 \(P\) 是一个大整数(\(1000 < P < 3,100,000\))。由于 \(2^P\) 是一个极大的数,直接计算不现实,需要利用数学公式和高精度技巧。

  2. 算法选择

    • 位数计算\(2^P\) 的位数 = \(\lfloor P \times \log_{10}2 \rfloor + 1\),因为 \(10^{k-1} \le 2^P < 10^k\)
    • 最后 500 位计算:只需计算 \(2^P\) 的最后 500 位,然后减 1。使用高精度乘法,但为了加速,将乘法分块:先计算 \(2^{32}\),然后循环乘以 \(2^{32}\) 若干次,最后再乘以 \(2\) 的余数次幂。
    • 复杂度\(O(500 \times (P/32 + P \bmod 32))\),对于 \(P \le 3.1\times10^6\),约 \(500 \times 10^5\) 次操作,可接受。
  3. 关键步骤

    • 位数:输出 (int)(p * log10(2) + 1)
    • 计算最后 500 位
      • 用数组 a[1..500] 存储每位数字(a[1] 为最高位,a[500] 为最低位)。
      • 初始 a[500] = 1(表示数值 1)。
      • \(P\) 分解为 num1 = P / 32num2 = P % 32
      • 先循环 num1 次,每次将整个数组乘以 \(2^{32}\)(即 a[j] *= 2^32,然后处理进位)。
      • 再循环 num2 次,每次将数组乘以 2。
      • 最后 a[500]--(处理减 1 可能导致的借位,但此处因为只影响最后一位,简单处理即可)。
    • 输出:按每行 50 位输出数组 a[1..500]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(500 \times (P/32 + P \bmod 32))\)
    • 空间复杂度:\(O(500)\)
  5. 高精度与对数

    • 位数公式:使用对数计算位数是处理大数位数的标准方法。
    • 分块乘法:用 \(2^{32}\) 作为基数,大幅减少乘法次数(从 \(P\) 次减少到 \(P/32\) 次)。
    • 进位处理:从低位到高位依次处理进位,确保每位数在 0~9 之间。
    • 最后 500 位补零:如果实际位数不足 500,高位补 0,这正好与数组初始化为 0 一致。
    • 适用场景:这类“大数幂的位数和末几位”问题,是数学与高精度结合的经典题型。

【算法标签】

普及 #高精度

【代码详解】

#include <iostream>
#include <cmath>
using namespace std;

long long a[501] = {0}; // 数组a用于存储高精度计算结果,初始化为0

// 计算2的n次方
long long cp(int n)
{
    long long result = 1;
    for (int i = 1; i <= n; i++)
        result *= 2; // 每次乘以2
    return result; // 返回2的n次方
}

int main()
{
    int p; // 输入的指数p
    cin >> p; // 输入p

    // 将p分解为num1和num2,其中num1是p除以32的商,num2是余数
    int num1 = p / 32; // num1表示需要乘以2^32的次数
    int num2 = p - num1 * 32; // num2表示剩余的乘法次数(每次乘以2)

    long long x = cp(32); // 计算2^32的值
    a[500] = 1; // 初始化数组a的最后一位为1(表示初始值为1)

    /**
     * @brief
     * 为了加快计算速度,我们将乘法分为两部分:
     * 1. 前num1轮,每次乘以2^32
     * 2. 后num2轮,每次乘以2
     */

    // 前num1轮,每次乘以2^32
    for (int i = 1; i <= num1; i++)
	{
        // 对数组a的每一位乘以2^32
        for (int j = 500; j >= 1; j--)
            a[j] *= x;
        // 处理进位
        for (int j = 500; j >= 1; j--)
		{
            a[j - 1] += a[j] / 10; // 将进位加到前一位
            a[j] %= 10; // 当前位只保留个位数
        }
    }

    // 后num2轮,每次乘以2
    for (int i = 1; i <= num2; i++)
	{
        // 对数组a的每一位乘以2
        for (int j = 500; j >= 1; j--)
            a[j] *= 2;
        // 处理进位
        for (int j = 500; j >= 1; j--)
		{
            a[j - 1] += a[j] / 10; // 将进位加到前一位
            a[j] %= 10; // 当前位只保留个位数
        }
    }

    a[500]--; // 最后一位减1(因为2^p - 1)

    // 输出2^p - 1的位数
    cout << (int)(p * log10(2) + 1) << endl;

    // 输出2^p - 1的值,每50位换行
    for (int i = 1; i <= 500; i++)
	{
        cout << a[i];
        if (i % 50 == 0) // 每50位换行
            cout << endl;
    }

    return 0;
}

【运行结果】

1279
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087
posted @ 2026-08-31 09:23  团爸讲算法  阅读(6)  评论(0)    收藏  举报