P1045 [NOIP2003 普及组] 麦森数
直接纯模拟,然后想办法优化
- 如果直接模拟计算那么位数太大会超时数学方法
- 一次乘2^20次方
- 做题的时候想到的 (貌似叫做压位)
一般来说高精度原理,是把让数组的每一位表示数的位数,所以只要第一位大于10,就直接进位
如果每一次都这样进位的话,那么时间的开销会很大
那么就可以考虑当第一个数为10位数时再进位;这样会大大减少时间
注意细节
- 没有满500位需要输出前导零
- 其实如果长度大于500,就不需要再多计算,只需要计算500以内的数(也是种减少时间的方法
- 高精(压位)完之后 再高精一次,这时的高精需要按照十进制再进位(这里也就最大O(500))
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int p;
long long s[520];
int main(){
bool book=0;
scanf("%d",&p);
memset(s,0,sizeof(s));
s[1]=1;
int len=1;
int lim=p/20;
for(register int i=1;i<=lim;++i){//一次2^20次方
for(register int j=1;j<=len;++j)
s[j]*=1048576;//2的20次方
if(s[1]>=10000000000){
for(register int k=1;k<=len;++k)
if(s[k]>=10){
if(len==500)
book=1;
s[k+1]+=s[k]/10;
s[k]%=10;
if(s[len+1]!=0 && book==0) ++len;
}
}
}
if(p%20!=0){//还有不满20的次数就一个一个*
for(register int i=1;i<=p%20;++i){
for(register int j=1;j<=len;++j)
s[j]*=2;
if(s[1]>=10000000000){//压位
for(register int k=1;k<=len;++k)
if(s[k]>=10){
if(len==500)
book=1;
s[k+1]+=s[k]/10;
s[k]%=10;
if(s[len+1]!=0 && book==0) ++len;
}
}
}
}
for(register int k=1;k<=len;++k)//十进制高精
if(s[k]>=10){
if(len==500)
book=1;
s[k+1]+=s[k]/10;
s[k]%=10;
if(s[len+1]!=0 && book==0) ++len;
}
s[1]-=1;
int ans=(int)p*log10(2)+1;//位数
printf("%d\n",ans);
if(ans<500){
register int j=1;
for(register int i=ans+1;i<=500;++i,++j){
printf("0");
if(j%50==0) printf("\n");
}
for(register int i=ans;i>=1;--i,++j){
printf("%lld",s[i]);
if(j%50==0) printf("\n");
}
}
else{
for(register int i=500,j=1;i>=1;--i,++j){
printf("%lld",s[i]);
if(j%50==0)printf("\n");
}
}
//输出
return 0;
}