【题解】【高精度】—— [NOIP2003 普及组] 麦森数

[NOIP2003 普及组] 麦森数

题目描述

形如 2 P − 1 2^{P}-1 2P1 的素数称为麦森数,这时 P P P 一定也是个素数。但反过来不一定,即如果 P P P 是个素数, 2 P − 1 2^{P}-1 2P1 不一定也是素数。到 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 2P1 的位数和最后 500 500 500 位数字(用十进制高精度数表示)

输入格式

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

输出格式

第一行:十进制高精度数 2 P − 1 2^{P}-1 2P1 的位数。

2 ∼ 11 2\sim 11 211 行:十进制高精度数 2 P − 1 2^{P}-1 2P1 的最后 500 500 500 位数字。(每行输出 50 50 50 位,共输出 10 10 10 行,不足 500 500 500 位时高位补 0 0 0

不必验证 2 P − 1 2^{P}-1 2P1 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>10k1,那么它的位数就是 k k k

    又因为 2 p 2^p 2p 的最后一位数永远不可能是0,不可能-1之后退位导致位数减少。所以我们将求 2 p − 1 2^p-1 2p1 的位数转换成求 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>10k1

将不等式变个形 k > l o g 10 ( 2 p ) > k − 1 k>log_{10}(2^p)>k-1 k>log10(2p)>k1

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)>k1
那么 p × l o g 10 ( 2 ) p\times log_{10}(2) p×log10(2)向下取整就等于 k − 1 k-1 k1。所以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;
}

喜欢就订阅此专辑吧!

【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。

欢迎扫码关注蓝胖子编程教育
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子教编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值