洛谷P1045 麦森数
题目描述
求\(2^P - 1\)的后五百位,且输出\(2^P - 1\)的位数,其中\(1000<P<3100000\)
输出
第一行:十进制高精度数 \(2^{P}−1\) 的位数。
第2-11行:十进制高精度数 \(2^{P}−1\) 的最后500位数字。
(每行输出50位,共输出10行,不足500位时高位补0)
不必验证 \(2^{P}−1\) 与\(P\)是否为素数。
分析
1. 求数的位数
假设\(x = 2^P - 1\),即求\(x\)的位数,因为\(2^P\)的最后一位一定大于1。
所以\(2^P\)的位数与\(x\)的位数相同。
\(len = lg{x} + 1 = lg{2^P} + 1 = P*lg{2} + 1\)
例如100
的长度为\(lg100 + 1 = 2 +1 = 3\)
所以长度的计算方法为
len = (int)(p * lg10(2)) + 1
2. 两个高精度数的乘法
const int N = 1e3 + 10; // 因为输出的长度为500, 500 + 500 = 1000
vector<int> mul(vector<int> A, vector<int> B) {
vector<int> C(N, 0);
for (i = 0; i < 500; i++)
for (j = 0; j < 500; j++)
C[i + j] += C[i] * C[j];
for (j = 0; j < 500; j++) {
C[j + 1] += C[j] / 10;
C[j] = C[j] % 10;
}
return C;
}
3. 快速幂
使用快速幂对空间复杂度进行优化
int p; // p为输入的阶
vector<int> t(N, 0); // 保存2^1 2^2 2^4
vector<int> res(N, 0); // 结果
t[0] = 2; // 初始化
res[0] = 1;
while (p) {
if (p & 1) // 如果p的2进制最后一个位为1
res = mul(res, t);
t = mul(t * t);
p >>= 1;
}
4. 输出
for (int i = 0, k = 499; i < 10; i++) {
for (int j = 0; j < 50; j++, k--) {
if (k) printf("%d", res[k]);
else printf("%d", res[k] - 1);
}
puts("");
}
完整代码
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int N = 1000 + 10;
vector<int> mul(vector<int> &A, vector<int> &B) {
vector<int> C(N, 0);
for (int i = 0; i < 500; i++)
for (int j = 0; j < 500; j++)
C[i + j] += A[i] * B[j];
// printf("%d ", i + j);
for (int j = 0; j < 500; j++) {
C[j + 1] += C[j] / 10;
C[j] %= 10;
}
return C;
}
int main() {
int p;
scanf("%d", &p);
vector<int> res(N, 0);
vector<int> t(N, 0);
t[0] = 2; res[0] = 1;
printf("%d\n", (int)(p * log10(2)) + 1);
while (p) {
if (p & 1)
res = mul(res, t);
t = mul(t, t);
p >>= 1;
}
for (int i = 0, k = 499; i < 10; i++) {
for (int j = 0; j < 50; j++, k--) {
if (k) printf("%d", res[k]);
else printf("%d", res[k] - 1);
}
puts("");
}
return 0;
}