zoukankan      html  css  js  c++  java
  • [NOIP2003普及组]麦森数(快速幂+高精度)

    [NOIP2003普及组]麦森数(快速幂+高精度)

    Description

    形如2^P-1的素数称为麦森数,这时P一定也是个素数。但反过来不一定,即如果P是个素数,2^P-1不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P=3021377,它有909526位。麦森数有许多重要应用,它与完全数密切相关。
    任务:从文件中输入P(1000 < P < 3100000),计算2^P-1的位数和最后500位数字(用十进制高精度数表示)

    Input

    只包含一个整数P(1000 < P < 3100000)

    Output

    第一行:十进制高精度数2^P-1的位数。
    第2-11行:十进制高精度数2^P-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)
    不必验证2^P-1与P是否为素数。

    Sample Input

    Sample Output

    题目大意:计算2^P-1的位数和它的最后500位并输出。
    1、计算位数:2^P-1的位数为[log10(2^P)]+1==[Plog10(2)]+1
    2、计算最后500位:高精度预处理2^i,计算时将P分解成二进制形式,然后相乘,只保留500位
    3、网站上的似乎和原题不太一样,原题要求每输出50位换行

    倍增快速幂+高精(果然什么东西跟高精扯上就恶心了)

    只保留最后500位,前面的直接卡掉

    位数可以直接算出来,n进制数M的位数是log(n)M,那么log(10)2^p=p*log(10)2=p*(log(2)/log(10))

    1、肯定需要高精度,毕竟500位

    2、普通乘法肯定超时,乘法次数*位数

    因为位数=plog(10)2=310W*log2

    操作次数为=310W

    所以普通乘法的复杂度为:n^2,这肯定要炸的不能在炸。 

    如果是保留500位,那也是500*310W,也要超,所以这里一定要用到快速幂

    本题分两问,第一问求位数,可以证明:当x有n位时,必有10^(n-1)<=x<10^n(如x有3位时必有100=10^2<=x<1000=10^3),取常用对数,n-1<=lgx<n,即lgx的整数部分是n-1,也就是说数x的位数是lg(x)的整数部分+1。故欲求x的位数只需求floor(log10(x)+1).

    然后第二问是去掉了取模运算、用上高精度的快速幂:

     1 #include <bits/stdc++.h>
     2 using namespace std; 
     3 void mul(int x[],int y[]);
     4 int main()
     5 {
     6     long p;
     7     int num=0;
     8     int ans[501]={0},a[501]={0},i;
     9     freopen("mason.in","r",stdin); 
    10     freopen("mason.out","w",stdout);
    11     scanf("%ld",&p);
    12     //第一问ok 
    13     num=(int)floor(p*log10(2)+1);
    14     printf("%d
    ",num);
    15     
    16     /*快速幂求2^p,同时用高精度乘法*/
    17     ans[1]=1; a[1]=2;
    18     while(p>0)
    19     {
    20         if(p&1)      //if(p%2==1)
    21             mul(ans,a);
    22         p=p>>1;      //p=p/2;
    23         mul(a,a);    //a*a -> a
    24     }
    25     ans[1]-=1;     //这个地方其实直接减1会有bug。当ans[1]为0的时候是错误的结果。所以可以采用下面的方法减1。但是对这个题目而言,2^p-1必然是奇数,也即个位是不可能为0。(ans[1]不会为0.) 
    26     /*for(i=1;i<=500;i++)
    27     {
    28         if(ans[i]>0) {ans[i]--;break;}
    29     }
    30     for(;i>=1;i--) ans[i]=9;*/
    31     for(i=500;i>0;i--) {printf("%d",ans[i]); if((i-1)%50==0) printf("
    ");}
    32     printf("
    ");
    33     return 0;
    34 }
    35 void mul(int x[],int y[])
    36 {
    37     /*   x*y->x   */
    38     int tmp[520]={0},lx=500,ly=500,i,j,len;  //tem[]一定要清零。
    39     //memset(tmp,0,sizeof(tmp));
    40     while(x[lx]==0&&lx>0) lx--; //计算x首位位置
    41     while(y[ly]==0&&ly>0) ly--; //计算y首位位置
    42     len=lx+ly;
    43     for(i=1;i<=ly;i++)
    44        for(j=1;j<=lx;j++)
    45            if(i+j-1<=500) tmp[i+j-1]+=y[i]*x[j];   //if语句是保证只保留500位 
    46     for(i=1;i<=500;i++) 
    47     {
    48         tmp[i+1]+=tmp[i]/10; //把进位的值加到高位 
    49         tmp[i]%=10;
    50         /*if(i<500&&tmp[i+1]==0)
    51         {len=i; break;}*/  //这个地方没理解其用意,似乎只是优化循环次数。但len没有使用的机会,所以干脆删除掉比较好。
    52     }  
    53     for(i=500;i>0;i--) x[i]=tmp[i];  //把结果复制到x数组 
    54 }
  • 相关阅读:
    ASP.NET控制cookie
    asp.net中Cookie的用法
    乱码问题
    JSP批量删除复选框选中记录
    JSP中根据时间查询
    关于<c:if>没有<c:else>解决方案
    Div
    Apache PIO 操作Excel
    将查询到的数据存到excel并下载
    做一个超链接,打开一个新窗口而保留原来的窗口
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/8142671.html
Copyright © 2011-2022 走看看