zoukankan      html  css  js  c++  java
  • 洛谷 P1045 & [NOIP2003普及组] 麦森数(快速幂)

    题目链接

    https://www.luogu.org/problemnew/show/P1045

    题目大意

    本题目的主要意思就是给定一个p,求2p-1的位数和后500位数。

    解题思路

      首先看一下数据范围,我们不难发现此题必须要用高精度来做。但是每一次高精度乘法的复杂度是o(n)的(n为数字的位数),所以很显然需要加一个快速幂。但是事实证明快速幂+高精度也会超时,所以我们必须进一步优化时间。

      根据题意,我们可知,只需要记录下后500位数即可,这里牵扯到一点点数论的知识,这一个数字的后500位是与500位以外的数是没有一点关系的,根据这个性质,我们就可以开一个500大的数组,每一次记录后500位的数字即可。

      但是此题还有一个难点,就是求总的位数,这里要运用数论知识。

      我们不难发现,2n的最后一位不可能是0,所以2p和2p-1的位数是一样的,我们只需求出2p的位数即可。

      我们知道,如果某个数是10n,那么这个数就有n+1位数字。我们知道,log10(2)意思是10的多少次方=2,所以10log10(2)=2。所以我们把2p写成(10log10(2) )p再根据幂的平方运算法则写成10log10(2)*p,而10log10(2)*p的位数是log10(2)*p+1,也就是2p的位数是log10(2)*p+1,也就是2p-1的位数是log10(2)*p+1。

      最后一定要注意输出格式!!本人因此爆零。。

    附代码

     1 #include<iostream>                    //快速幂:2的p次方=(2的平方)的p/2次方 = ((2的平方)的平方)的 p/2/2次方...... 
     2 #include<cstdio>                    //       f存的就是底数——2,2的2次方,2的二次方的二次方...... 
     3 #include<cmath>                        //       res 存的就是最后的答案 
     4 #include<cstring>                    //        sav是每一次高精度乘法的临时数组 
     5 using namespace std;
     6 int p,f[510],res[510],sav[510];                //数组开500多一点已足够 
     7 void rr1() {                                //rr1是将答案更新,乘上现在的底数 
     8     memset(sav,0,sizeof(sav));                 
     9     for(int i = 1;i <= 500; i++)            //动手画一下 ,列一个竖式,就会发现[i]*[j]得到的数字其实是[i+j-1]位上的数字 
    10         for(int j = 1;j <= 500; j++)        //这里只是乘,还没有进位 
    11             if(i+j<=505) sav[i+j-1] += res[i] * f[j];
    12     for(int i = 1;i <= 500; i++) {             //进位 
    13         sav[i+1] += sav[i] / 10;
    14         sav[i] %= 10;
    15     }
    16     memcpy(res,sav,sizeof(res));            //把求出来的数组sav更新到res中 
    17 }
    18 void rr2() {                                //rr2求得是底数的平方的值,存在f中 
    19     memset(sav,0,sizeof(sav));                //同上 
    20     for(int i = 1;i <= 500; i++) 
    21         for(int j = 1;j <= 500; j++)
    22             if(i+j<=505)sav[i+j-1] += f[i] * f[j];
    23     for(int i = 1;i <= 500; i++) { 
    24         sav[i+1] += sav[i] / 10;
    25         sav[i] %= 10;
    26     }
    27     memcpy(f,sav,sizeof(f));                
    28 }
    29 int main() {
    30     scanf("%d",&p);
    31     printf("%d\n",(int)(log10(2) * p + 1));    //先输出位数 
    32     res[1] = 1;
    33     f[1] = 2;                                //初始化要为2,否则会一直乘1 
    34     while(p != 0) {                            //快速幂 
    35         if(p % 2 == 1) rr1();                //任何一个数一直/2最后一定是1,这样就能确保更新答案 
    36         p /= 2;                                //rr1,rr2为高精度乘法
    37         rr2();                                //每一次都更新一遍f,也就是底数的平方 
    38     }
    39     res[1] -= 1;
    40     for(int i = 500;i >= 1; i--)
    41         if(i != 500 && i % 50 == 0) printf("\n%d",res[i]); //注意输出格式。 
    42         else printf("%d",res[i]);
    43     return 0;
    44 }
    AC代码

     //NOIP2003普及组t4

  • 相关阅读:
    LeetCode: LRU Cache
    LeetCode: Reorder List
    LeetCode: Linked List Cycle I && II
    LeetCode: Word Break I && II
    LeetCode: Single Number I && II
    太坑了,mybatis注解一对多,id没了
    ajax请求参数的格式
    查询结果拼接
    id拼接保存到单个字段后作为表连接的查询条件
    seam的定时轮巡
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/10463850.html
Copyright © 2011-2022 走看看