洛谷 P1045 [NOIP2003 普及组] 麦森数

题前建议

在做本题之前建议先学会高精度加法、快速幂、log函数的使用

题目描述

形如 −1的素数称为麦森数,这时 𝑃 一定也是个素数。但反过来不一定,即如果 𝑃 是个素数,2^{P}−1不一定也是素数。到 1998 年底,人们已找到了 37 个麦森数。最大的一个是 𝑃=3021377,它有 909526 位。麦森数有许多重要应用,它与完全数密切相关。

任务:输入 𝑃(1000<𝑃<3100000),计算 2^{P}−1 的位数和最后 500500 位数字(用十进制高精度数表示)

输入格式

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

输出格式

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

第 2∼11 行:十进制高精度数 2^{P}−1 的最后 500500 位数字。(每行输出 5050 位,共输出 1010 行,不足 500500 位时高位补 00)

不必验证 2^{P}−1 与 𝑃 是否为素数。

输入输出样例

输入 #1

1279

输出 #1

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

具体讲解

对于本题是一个高精度快速幂的问题,但中间需要一些技巧。

首先,高精度快速幂我就不讲了需要的同学可以看一下代码。

高精乘法:

vector<int> mul(vector<int> a,vector<int> b)
{
	vector<int> c(a.size()+b.size()+1);
	for(int i=0;i<a.size();++i)
	{
        for(int j=0;j<b.size();++j)
		{
			c[i+j]+=a[i]*b[j];
            c[i+j+1]+=c[i+j]/10;
            c[i+j]%=10;
        }
	}
	while(c.size()>1 && c.back()==0) c.pop_back();
	return c;
}//传入的a、b和返回的c皆为倒着的

快速幂:

int binpow(int a,int b)
{
	int res=1;
	while(b)
	{
    	if(b&1)
		{
			res*=a;
		}
    	a=a*a;
    	b>>=1;
  	}
 	return res;
}

注意:本题的p(1000<𝑃<3100000),不用使用高精度。

将他俩结合:

vector<int> mul(vector<int> a,vector<int> b)
{
	vector<int> c(a.size()+b.size()+1);
	for(int i=0;i<a.size();++i)
	{
        for(int j=0;j<b.size();++j)
		{
			c[i+j]+=a[i]*b[j];
            c[i+j+1]+=c[i+j]/10;
            c[i+j]%=10;
        }
	}
	while(c.size()>1 && c.back()==0) c.pop_back();
	return c;
}
vector<int> qpow(vector<int> a,int b) {
	vector<int> res;
	res.push_back(1);
	while(b)
	{
    	if(b&1)
		{
			res=mul(res,a);
		}
    	a=mul(a,a);
    	b>>=1;
  	}
	
 	return res;
}

由此小伙伴们应该可以写出一段暴力的50TLE代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
vector<int> mul(vector<int> a,vector<int> b)
{
	vector<int> c(a.size()+b.size()+1);
	for(int i=0;i<a.size();++i)
	{
        for(int j=0;j<b.size();++j)
		{
			c[i+j]+=a[i]*b[j];
            c[i+j+1]+=c[i+j]/10;
            c[i+j]%=10;
        }
	}
	while(c.size()>1 && c.back()==0) c.pop_back();
	return c;
}
vector<int> qpow(vector<int> a,int b) {
	vector<int> res;
	res.push_back(1);
	while(b)
	{
    	if(b&1)
		{
			res=mul(res,a);
		}
    	a=mul(a,a);
    	b>>=1;
  	}
	
 	return res;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;
	vector<int> sum;
	sum.push_back(2);
	cin>>n;
	sum=qpow(sum,n);
	cout<<sum.size()<<"\n";
	while(sum.size()<500)
	{
		sum.push_back(0ll);
	}
	for(int i=499;i>=0;--i)
	{
		if(i==0)
		{
			cout<<sum[0]-1;
			return 0;
		}
		cout<<sum[i];
		if((i)%50==0 && i!=499)
		{
			cout<<"\n";
		}
	}

	
	return 0;
}

于是小伙伴们可以想到:最后只要输出500位但我们却因为长度多算了很多,所以我们可以使用计算机的log10/log函数简化一下:

如果使用log函数,大家要使用换底公式比较麻烦,所以建议使用log10函数可以方便一点,这样我们就可以写出AC代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
vector<int> mul(vector<int> a,vector<int> b)
{
	vector<int> c(a.size()+b.size()+1);
	for(int i=0;i<a.size();++i)
	{
        for(int j=0;j<b.size();++j)
		{
			c[i+j]+=a[i]*b[j];
            c[i+j+1]+=c[i+j]/10;
            c[i+j]%=10;
        }
	}
	while(c.size()>1 && c.back()==0) c.pop_back();
	return c;
}
vector<int> qpow(vector<int> a,int b) {
	vector<int> res;
	res.push_back(1);
	while(b)
	{
    	if(b&1)
		{
			res=mul(res,a);
		}
    	a=mul(a,a);

    	b>>=1;
		while(res.size()>500)
		{
			res.pop_back();
		}
		while(a.size()>500)
		{
			a.pop_back();
		}
  	}
	
 	return res;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n;
	vector<int> sum;
	sum.push_back(2);
	cin>>n;
	sum=qpow(sum,n);
	cout<<(int)(log10(2)*n+1)<<"\n";//或cout<<(int)(log(2)*n/log(10))+1<<"\n";
	while(sum.size()<500)
	{
		sum.push_back(0ll);
	}
	for(int i=499;i>=0;--i)
	{
		if(i==0)
		{
			cout<<sum[0]-1;
			return 0;
		}
		cout<<sum[i];
		if((i)%50==0 && i!=499)
		{
			cout<<"\n";
		}
	}

	
	return 0;
}

好了今天这道比较综合的题目就OK了如果高精度不熟练的小伙伴们可以做一下:

洛谷 P1932 A+B A-B A*B A/B A%B Problem(A+B A-B A*B A/B A%B Problem - 洛谷

goodbye😊😎

  • 35
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值