1. 程式人生 > >【高精度】麥森數 NOIP 2003

【高精度】麥森數 NOIP 2003

[NOIP2003]麥森數

時間限制: 1 Sec  記憶體限制:64 MB

題目描述

形如2^p-1的素數稱為麥森數,這時p一定也是個素數。但反過來不一定,即如果p是個素數,2^p-1不一定也是素數。到1998年底,人們已找到了37個麥森數。最大的一個是p=3021377,它有909526位。麥森數有許多重要應用,它與完全數密切相關。現要求輸入p(1000< P < 3100000),計算2^p-1的位數和最後500位數字(用十進位制高精度數表示)。

輸入

第1行:1個整數p(1000

輸出

第1行:十進位制高精度數2^p-1的位數;第2..11行:十進位制高精度數2^p-1的最後500位數字(每行輸出50位,共輸出10行,不足500位時高位補0); 注意:不必驗證2^p-1與p是否為素數。

樣例輸入

(如果複製到控制檯無換行,可以先貼上到文字編輯器,再複製)

1279

樣例輸出

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087 

剛開始看到這麼一大堆(tuÓ)輸出,其實我是拒絕的。

想了想,第一,高精度;第二,求p次冪要用快速冪(瞭解快速冪詳見點這裡!!)。

一、首先我們要知道怎麼存500位的數,那麼就存在一個結構體數組裡面,進行高精度乘法的時候,就可以想一想豎式是怎麼計算的。但是想一想豎式是從最低位開始乘起來的,所以我用的陣列是倒序存數,最後輸出只要倒回來輸出就行了。

這裡用到了過載運算子:

big operator *(big &ys)const
	{big ans;
		
		for(int i=1;i<=len;i++)
			for(int j=1;j<=ys.len;j++)
			{
				if(i+j-1>500) break;//如果位數>500,不用考慮
				ans.A[i+j-1]+=A[i]*ys.A[j];//為什麼是i+j-1,
                                                           //自己想想豎式計算
                                if(ans.A[i+j-1]>=10)//向後進位
				{
					ans.A[i+j]+=ans.A[i+j-1]/10;
					ans.A[i+j-1]%=10;
				}
			}
		for(int i=500;i>=1;i--)//求位數
                        if(ans.A[i]!=0){ans.len=i;break;}
                return ans;
	}

二、進行冪運算的時候,要把p進行二進位制劃分(自己看上面的連結)。

三、程式碼如下:

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

int p;

struct big
{
	int A[501],len;//A存數,len存位數
	big(){memset(A,0,sizeof A);len=0;}//清0
	big operator *(big &ys)const//過載乘號
	{big ans;
		
		for(int i=1;i<=len;i++)
			for(int j=1;j<=ys.len;j++)
			{
				if(i+j-1>500) break;
				ans.A[i+j-1]+=A[i]*ys.A[j];
				if(ans.A[i+j-1]>=10)
				{
					ans.A[i+j]+=ans.A[i+j-1]/10;
					ans.A[i+j-1]%=10;
				}
			}
		for(int i=500;i>=1;i--)
			if(ans.A[i]!=0){ans.len=i;break;}
		return ans;
	}
}danwei,ans;


int main()
{
	scanf("%d",&p);
	danwei.A[1]=2;danwei.len=1;//danwei即使底數
	ans.A[1]=1;ans.len=1;
	printf("%d\n",int(p*log10(2))+1);
	while(p)//快速冪
	{
		if(p&1) ans=ans*danwei;
		p/=2;
		danwei=danwei*danwei;
	}
	ans.A[1]--;//求的是2^p-1,所以最後--
	for(int i=1;i<=10;i++)
	{
		for(int j=1;j<=50;j++)
			printf("%d",ans.A[501-(i-1)*50-j]);//倒序輸出
		printf("\n");
	}
	
}