【题解】【高精度】—— [NOIP2003 普及组] 麦森数
- [NOIP2003 普及组] 麦森数
- 题目描述
- 输入格式
- 输出格式
- 输入输出样例
- 输入 #1
- 输出 #1
 
- 提示
 
- 1.题意解析
- 1.1.求位数
- 1.2.输出最后500位
- 1.2.1.输出
- 1.2.2.高精度乘法
- 1.2.3.快速幂
 
 
- 2.AC代码
[NOIP2003 普及组] 麦森数
题目描述
形如 2 P − 1 2^{P}-1 2P−1 的素数称为麦森数,这时 P P P 一定也是个素数。但反过来不一定,即如果 P P P 是个素数, 2 P − 1 2^{P}-1 2P−1 不一定也是素数。到 1998 年底,人们已找到了 37 个麦森数。最大的一个是 P = 3021377 P=3021377 P=3021377,它有 909526 位。麦森数有许多重要应用,它与完全数密切相关。
任务:输入 P ( 1000 < P < 3100000 ) P(1000<P<3100000) P(1000<P<3100000),计算 2 P − 1 2^{P}-1 2P−1 的位数和最后 500 500 500 位数字(用十进制高精度数表示)
输入格式
文件中只包含一个整数 P ( 1000 < P < 3100000 ) P(1000<P<3100000) P(1000<P<3100000)
输出格式
第一行:十进制高精度数 2 P − 1 2^{P}-1 2P−1 的位数。
第 2 ∼ 11 2\sim 11 2∼11 行:十进制高精度数 2 P − 1 2^{P}-1 2P−1 的最后 500 500 500 位数字。(每行输出 50 50 50 位,共输出 10 10 10 行,不足 500 500 500 位时高位补 0 0 0)
不必验证 2 P − 1 2^{P}-1 2P−1 与 P P P 是否为素数。
输入输出样例
输入 #1
1279
输出 #1
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087
提示
【题目来源】
NOIP 2003 普及组第四题
1.题意解析
我们把这道题分成两个模块。一个是求位数,另一个是输出最后500位。
1.1.求位数
我们知道,一个数 1 0 k > n > 1 0 k − 1 10^k>n>10^{k-1} 10k>n>10k−1,那么它的位数就是 k k k。
    又因为  
     
      
       
        
        
          2 
         
        
          p 
         
        
       
      
        2^p 
       
      
    2p 的最后一位数永远不可能是0,不可能-1之后退位导致位数减少。所以我们将求  
     
      
       
        
        
          2 
         
        
          p 
         
        
       
         − 
        
       
         1 
        
       
      
        2^p-1 
       
      
    2p−1 的位数转换成求  
     
      
       
        
        
          2 
         
        
          p 
         
        
       
      
        2^p 
       
      
    2p 的位数。
接下来我们设 2 p 的位数 = k 2^p的位数=k 2p的位数=k,则有 1 0 k > 2 p > 1 0 k − 1 10^k>2^p>10^{k-1} 10k>2p>10k−1
将不等式变个形 k > l o g 10 ( 2 p ) > k − 1 k>log_{10}(2^p)>k-1 k>log10(2p)>k−1
l o g x ( n ) log_x(n) logx(n) 表示 x k = n x^k=n xk=n中的 k k k,不理解可以自己上网去了解一下对数相关知识。
再根据对数的运算法则,可以得出 
      
       
        
        
          k 
         
        
          > 
         
        
          p 
         
        
          × 
         
        
          l 
         
        
          o 
         
         
         
           g 
          
         
           10 
          
         
        
          ( 
         
        
          2 
         
        
          ) 
         
        
          > 
         
        
          k 
         
        
          − 
         
        
          1 
         
        
       
         k>p\times log_{10}(2)>k-1 
        
       
     k>p×log10(2)>k−1
 那么 
     
      
       
       
         p 
        
       
         × 
        
       
         l 
        
       
         o 
        
        
        
          g 
         
        
          10 
         
        
       
         ( 
        
       
         2 
        
       
         ) 
        
       
      
        p\times log_{10}(2) 
       
      
    p×log10(2)向下取整就等于 
     
      
       
       
         k 
        
       
         − 
        
       
         1 
        
       
      
        k-1 
       
      
    k−1。所以int(p*log10(2)+1)就是最终的答案。
1.2.输出最后500位
    接下来就要用到高精度了。还是老样子,使用我在高精度算法解析和高精度赛场用模板分装的bigint结构体。不过有两个地方要稍微改一下。
1.2.1.输出
    这里由于只要求输出最后的500位。所以我们可以从1~500遍历输出。每输出50个数,即i%50==0就输出一个换行。
friend void print(bigint x)//输出,和普通的输出高精度整数略有不同 
{
	for(int i=1;i<=500;i++)//这里只输出最后500位
	{
		cout<<x[501-i];//500-i+1的化简
		if(i%50==0)cout<<endl;//每输出50位就输出一个换行 
	}
}
1.2.2.高精度乘法
这里只需要计算最后500位。展开时也最多展开500+500位。改进后的代码如下:
friend bigint operator*(bigint a,bigint b)//乘法,这里只计算最后500位 
{
	bigint c;
	int lena=a.len,lenb=b.len;
	for(int i=1;i<=min(lena,500);i++)//最多计算500位 
		for(int j=1;j<=min(lenb,500);j++)//同理 
		    c[i+j-1]+=a[i]*b[j];
	c.flatten(min(lena+lenb,1000));//最多展开500+500位 
	return c;
}
1.2.3.快速幂
对于计算 2 p 2^p 2p,我们可以使用快速幂。这里直接给出快速幂模版。如果还不知道快速幂,可以去洛谷P1226 【模板】快速幂练练手。
bigint qpow(bigint a,int p)//快速幂
{
	if(p==1)return a;
	if(p%2==0)return qpow(a*a,p/2);
	return a*qpow(a,p-1);
}
2.AC代码
#include<bits/stdc++.h>
using namespace std;
struct bigint
{
	int a[1010],len;
	bigint(int x=0)
	{
		memset(a,0,sizeof(a));
		if(x==0)
		{
			len=1;
			return;
		}
		for(len=1;x;len++)
		    a[len]=x%10,x/=10;
		len--;
	}
	void flatten(int L)
	{
		len=L;
		for(int i=1;i<=len;i++)
		{
			a[i+1]+=a[i]/10;
			a[i]%=10;
		}
		while(!a[len])
		    len--;
	}
	int &operator[](int i)
	{
		return a[i];
	}
	friend void print(bigint x)//输出,和普通的输出高精度整数略有不同 
	{
		for(int i=1;i<=500;i++)//这里只输出最后500位
		{
			cout<<x[501-i];//500-i+1的化简
		    if(i%50==0)cout<<endl;//每输出50位就输出一个换行 
		}
	}
    //这里无需重载+
	friend bigint operator*(bigint a,bigint b)//乘法,这里只计算最后500位 
	{
		bigint c;
		int lena=a.len,lenb=b.len;
		for(int i=1;i<=min(lena,500);i++)//最多计算500位 
		    for(int j=1;j<=min(lenb,500);j++)//同理 
		        c[i+j-1]+=a[i]*b[j];
		c.flatten(min(lena+lenb,1000));//最多展开500+500位 
		return c;
	}
};
bigint qpow(bigint a,int p)//快速幂 
{
	if(p==1)return a;
	if(p%2==0)return qpow(a*a,p/2);
	return a*qpow(a,p-1);
}
int main()
{
    int p;
    bigint a;
    cin>>p;
    cout<<int(p*log10(2)+1)<<endl;//输出位数
    a=qpow(2,p);a[1]--;//计算2^p-1
    print(a);//输出 
	return 0;
}
喜欢就订阅此专辑吧!
【蓝胖子编程教育简介】
 蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。
欢迎扫码关注蓝胖子编程教育
 



















