NOI4.4.1708 麦森数 题解(C++)

NOI4.4.1708 麦森数 题解(C++)

先来个动图乐呵乐呵。
Java和C++的区别:

纯属玩笑。。。

好久没写题解了,今天来一道经典的分治题——麦森数。

题目传送门

请听题:
1708:麦森数
总时间限制: 1000ms 内存限制: 65536kB
描述

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

输入
文件中只包含一个整数P(1000
输出
第1行:十进制高精度数2p-1的位数。
第2-11行:十进制高精度数2p-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0
不必验证2p-1与P是否为素数。 (多么令人高兴啊)

样例输入和输出作者不提供,太多了~

首先,你可以非常明确的知道这一题要用分治+高精度来解决,毕竟21000-1还是最小的。
输出的第一行就已经让人很头痛了,只不过有一个相当好用的公式直接输出。请看下面:

floor(log(2)/log(10)*n+1)

这是什么意思呢?就是指一个二进制位占十进制位中多少位,再乘以次方数(n),最后加一且向下取整就OK了。就是酱紫的。
我们可以试一试能不能输出正确的位数。

#include
#include
using namespace std;
int n;
int main(){
	cin>>n;
	cout<<(long long)floor(log(2)/log(10)*n+1)<<endl;
}

结果是这样的:

NOI4.4.1708 麦森数 题解(C++)_第1张图片
挺香,不是吗?
接着就是一个终极之问:为什么要用分治?
答:没为什么,开心而已。。。
经过一顿对作者拳打脚踢后
答:为了提升程序的速度,保证不超时。
使用分治的思路就是说类似:
2500 = 2250*2250= …
看上去蛮像递归的。
还有一个叫做高精度乘法的东西瑟瑟发抖,自己随便列一个两位数乘以3位数的算式就知道了。

直接看代码吧,一切尽在注释中:

#include
#include//log(),floor()应该都在这个头文件
using namespace std;
int out[505];
int g[100005]/*500位乘以500位,多开一点也不会超空间*/;
int k(int n){/*n指2的次方数*/
	if(n == 0){
		return 1;
	}//递归出口
	k(n/2);//调用下一级(=2^(n/2)^2)
	for(int i = 1;i<=1000;i++){
		g[i] = 0;
	}//清0,没什么好说的 
	for(int i = 1;i<=500/*因为只要500位*/;i++){
		for(int j = 1;j<=500/*同理*/;j++){
			g[i+j-1/*类似乘法的第几位*/] += out[i]*out[j];
		}
	}//这一段就是模拟乘法,超出500位的数字我们就可以直接不要了 
	if(n%2==1){
		for(int i = 1;i<=500;i++){
			g[i]*=2;
		}
	}//取余2不尽的就先乘以2,次方数减一 
	for(int i = 1;i<=500;i++){
		out[i] = g[i]%10;//只能取个位
		g[i+1] += g[i]/10;//进位 
	}
}
int main(){
	int n;
	cin>>n;
	cout<<(long long/*应该改成int也行*/)floor(log(2)/log(10)*n+1)<<endl;
	out[1] = 1;
	k(n);
	for(int i = 500;i>=2;i--){
		cout<<out[i];
		if(i%50==1){//控制50位1个回车
			cout<<endl;
		}
	}
	cout<<out[1]-1;
	//这个单独输出,因为这一位要减1,也不用怕退位,因为个位只有可能是2,4,8,6
}

本题的讲解到此结束,若有哪些地方有毛病或问题请提出。

你可能感兴趣的:(信息学,题解)