P1045 [NOIP2003 普及组] 麦森数

https://www.luogu.com.cn/problem/P1045

首先第一问,输出\(2^p-1\) 的位数

(1)自然数\(n\)的位数计算公式推导
\(n=10\) 位数是\(2\);
\(n=100\) 位数是\(3\);
\(n=1000\) 位数是\(4\);
\(n=12345\) 位数是\(5\);
小结:自然数\(n\)的位数等于\(\lfloor log_{10}(n) \rfloor +1\),就也是 \(\lfloor lg(n)+1 \rfloor\)

对数与指数的知识

cout << log10(12345) << endl;        //求10的多少次方是12345
cout << pow(10, 4.09149) << endl; //输出12345 ,表示10的4.09149次方是12345。

以上面的例子来说,\(lg(12345)+1= \lfloor lg(12345) \rfloor +1=\lfloor 4.09149 \rfloor +1\)=\(4+1\)=\(5\)

至此,问题转化为求:\(lg(2^p-1)+1\)的值 。

(2)\(2^p-1\)的位数与\(2^p\)位数的关系
考虑到\(2^p\)的个位只能是\(2,4,6,8\),不可能出现数字\(0\),所以\(2^p-1\)的位数与\(2^p\)位数是一样的。也就是说,我们只需要计算出\(lg(2^p)+1\)就可以知道答案了!

(3)计算\(lg(2^p)+1\)
这个玩意怎么算呢?利用数学公式\(lg(m^n)=m * lg(n)\)来算,就是\(p* lg(2)+1\),而\(lg(2)\)就是以\(10\)为底的\(2\)的对数,C++中有现成的计算函数(int) (p * log10(2) + 1)

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

/*
vector resize解析:
如果n小于当前容器的大小,则将内容减少到其前n个元素,并删除超出范围的元素(并销毁它们)。
如果n大于当前容器的大小,则通过在末尾插入所需数量的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将对它们进行值初始化。
如果n也大于当前容器容量,将自动重新分配已分配的存储空间。
请注意,此函数通过插入或擦除容器中的元素来更改容器的实际内容。
*/

//高精度乘法模板(高精乘高精)
vector<int> mul(vector<int> &A, vector<int> &B) {
    //只保留500个长度
    if(A.size()>500) A.resize(500);    
    if(B.size()>500) B.resize(500);

    int la = A.size(), lb = B.size();
    vector<int> C(la + lb + 10, 0);//提前申请结果所需的空间
    for (int i = 0; i < la; i++)
        for (int j = 0; j < lb; j++)
            C[i + j] += A[i] * B[j];

    for (int i = 0; i < C.size(); i++)
        if (C[i] >= 10) {
            C[i + 1] += C[i] / 10;
            C[i] %= 10;
        }
    //处理前导0
    while (C.size() > 1 && C.back() == 0)C.pop_back();

    //只保留500个长度
    if(C.size()>500) C.resize(500);
    return C;
}

//快速幂+高精度 a^b
vector<int> qmi(int a, int b) {
    vector<int> ans;
    ans.push_back(1);

    vector<int> A;
    A.push_back(a);
    while (b) {
        if (b & 1) ans = mul(ans, A);
        b >>= 1;
        A = mul(A, A);
    }
    return ans;
}

int main() {
    int p;
    cin >> p;
    vector<int> A = qmi(2, p);

    vector<int> B;
    B.push_back(1);

    A[0]--;

    //一共多少位
    printf("%d\n", (int) (p * log10(2) + 1));

    //每行输出50位,共输出10行,不足500位时高位补0
    int cnt = A.size();
    for (int i = 0; i < 500 - cnt; i++) A.push_back(0);

    //倒着输出
    for (int i = 500 - 1; i >= 0; i--) {
        printf("%d", A[i]);
        if (i % 50 == 0 && i < A.size() - 1) printf("\n");
    }
    return 0;
}
posted @ 2021-07-09 16:15  糖豆爸爸  阅读(109)  评论(0)    收藏  举报
Live2D