麦森数(分治或快速幂)
题目分析:
第一问是很简单的,只需要求一个对数而已,数学原理:十进制正整数n的位数为int(log10(n))+1。所以2^P-1的位数int(log10(2)*p)+1 。
2^p = 10^(p*log10(2) ) , 所以2^p的位数是p*log10(2)。
第二问的关键是高精度乘法和指数幂的运算,而且由于题目要求最后500位数字,所以在计算乘法的时候我们只要求计算乘数的低500位就好了。
指数幂的运算不能硬乘,而要采用分治算法,否则就超时了。分治递归算法求指数幂是非常经典的,其数学原理是a^n = a^(n/2)*a^(n/2)*f(a),其中f(a) = 1(n%2==0)或f(a) = a(n%2==1)。
另外我们也可以创建一个栈,记录每次执行(n /= 2)前n的值是奇数还是偶数,然后根据上面的数学原理,模仿递归的思路,从n=1或n=0开始逆向计算a^n。
采用递归算法的时候,由于存储高精度整数数组的大小是预置MAX = 1000,所以在调用递归函数的时候要按引用传递参数,否则到了后面空间就不够分配了。
为了满足“每行输出50位”的条件,我把存储高精度整数数组的元素设置成5位数,这样输出的时候只需每行输出10个元素就行了。
1 #include<iostream> 2 #include<cstdio> 3 #include <cctype> 4 #include<algorithm> 5 #include<cstring> 6 #include<cmath> 7 #include<string> 8 #include<cmath> 9 #include<set> 10 #include<vector> 11 #include<stack> 12 #include<queue> 13 #include<map> 14 using namespace std; 15 #define ll long long 16 #define mem(a,x) memset(a,x,sizeof(a)) 17 #define se second 18 #define fi first 19 const int INF= 0x3f3f3f3f; 20 const int N=1e6+5; 21 22 int p,a[505],b[1005]; 23 24 void quick(int x) 25 { 26 if(x==0) return; 27 quick(x/2); 28 mem(b,0); 29 for(int i=0;i<500;i++) 30 { 31 for(int j=0;j<500;j++) 32 { 33 if(x&1) b[i+j] += a[i]*a[j]*2; 34 else b[i+j] += a[i]*a[j]; 35 } 36 } 37 for(int i=0;i<500;i++) 38 { 39 a[i] = b[i]%10; 40 b[i+1]+= b[i]/10; 41 } 42 } 43 44 int main() 45 { 46 cin>>p; 47 a[0]=1; 48 quick(p); 49 50 a[0]--; 51 int k=0; 52 while(a[k]<0) a[k+1]--,a[k]+=10,k++; 53 54 cout<< floor( p*log10(2)+1 )<<endl; 55 for(int i=499;i>=0;i--) 56 { 57 cout<<a[i]; 58 if((i)%50==0) cout<<endl; 59 } 60 }
接下来 套一套快速幂的模板。。
1 #include<iostream> 2 #include<cstdio> 3 #include <cctype> 4 #include<algorithm> 5 #include<cstring> 6 #include<cmath> 7 #include<string> 8 #include<cmath> 9 #include<set> 10 #include<vector> 11 #include<stack> 12 #include<queue> 13 #include<map> 14 using namespace std; 15 #define ll long long 16 #define mem(a,x) memset(a,x,sizeof(a)) 17 #define se second 18 #define fi first 19 const int INF= 0x3f3f3f3f; 20 const int N=1e6+5; 21 22 int p,a[1005],b[1005],c[1005],f[1005],d[1005]; 23 24 void work1() 25 { 26 mem(c,0); 27 for(int i=0;i<500;i++) 28 { 29 for(int j=0;j<500;j++) 30 { 31 c[i+j] += a[i]*b[j]; //数组a就是原来快速幂模板中的ans, 数组b就是a 32 } 33 } 34 for(int i=0;i<500;i++) 35 { 36 f[i]=c[i]%10; 37 c[i+1]+=c[i]/10; 38 a[i]=f[i]; //更新a数组,即实现ans*=a的步骤 得到ans*=a后的ans 39 } 40 } 41 void work2() 42 { 43 mem(d,0); 44 for(int i=0;i<500;i++) 45 { 46 for(int j=0;j<500;j++) 47 { 48 d[i+j] += b[i]*b[j]; 49 } 50 } 51 for(int i=0;i<500;i++) 52 { 53 b[i]=d[i]%10; //更新b数组,即实现 a*=a的步骤 54 d[i+1]+=d[i]/10; 55 } 56 } 57 58 void quick(int x,int y) 59 { 60 a[0]=1; //ans 相当于给ans赋初值1 61 b[0]=x; // a 给a赋初值 2 62 while(y) 63 { 64 if(y&1) work1(); 65 y>>=1; 66 work2(); 67 } 68 } 69 70 int main() 71 { 72 cin>>p; 73 quick(2,p); 74 75 f[0]--; int k=0; 76 while(f[k]<0) f[k]+=10,f[k+1]--,k++; 77 78 cout<<floor(p*log10(2)+1) <<endl; 79 for(int i=499;i>=0;i--) 80 { 81 cout<<f[i]; 82 if(i%50==0) cout<<endl; 83 } 84 85 }