**题目:**https://www.luogu.org/problemnew/show/P1045
题解:
如果数据规模小一点完全可以用进制转换来做,但是数据在百万级别就决定了普通进制转换做法不能AC。因此优先考虑快速幂+高精度乘法做法或者压位+高精度乘法。
方法一:快速幂+高精度乘法做法:
#include <bits/stdc++.h>
using namespace std;
int f[1001],p,res[1001],sav[1001],s;//save[]是暂存计算过程的结果的数组
void result_1(){//单乘
memset(sav,0,sizeof(sav));
for(int i=1;i<=500;i++)//乘法无倒正
for(int j=1;j<=500;j++) sav[i+j-1]+=res[i]*f[j];
for(int i=1;i<=1000;i++){//处理进位
sav[i+1]+=sav[i]/10;
sav[i]=sav[i]%10;
}
//memcpy(res,sav,sizeof(res));//复制函数
for(int i=0;i<=1000;i++) res[i]=sav[i]; //存储结果
}
void result_2(){//自乘
memset(sav,0,sizeof(sav));
for(int i=1;i<=500;i++)//循环到500即可,不用到1000
for(int j=1;j<=500;j++) sav[i+j-1]+=f[i]*f[j];//乘法无倒正
for(int i=1;i<=1000;i++){
sav[i+1]+=sav[i]/10;
sav[i]=sav[i]%10;
}
for(int i=0;i<=1000;i++) f[i]=sav[i];//存储结果
}
int main(){
cin>>p;
s=(int)(log10(2)*p)+1;//log10是自带函数
res[1]=1;//初始化应该是1
f[1]=2;//重要初始化 f[]是2的p次方的结果,初始化应该是2而不是1
cout<<s<<endl;
while(p) {//计算2的p次方
if(p&1) result_1();//单乘
p>>=1;
result_2();//自乘
}
res[1]-=1;//2的p次方-1
for(int i=500;i>=1;i--){//输出
if(i<500&&i%50==0) cout<<endl;
cout<<res[i];
}
return 0;
}
方法二:压位+高精度乘法做法:
long long 的取值范围是:8字节8位:2^64=1844 6744 0737 0960 0000,因此,一次可以压10位
注意位运算的优先级 :算术运算-> 移位运算->位运算 (逻辑运算)
#include <bits/stdc++.h>
using namespace std;
long long a[51]={0,1},p,i,j,s,k,top=1;
int main(){
cin>>p;
s=p*log10(2)+1;
cout<<s<<endl;
while(p>=20){
k=0;
for(i=1;i<=50&&i<=top;i++){
a[i]=(a[i]<<20)+k;//数组每个元素放10位数
k=a[i]/10000000000;
a[i]%=10000000000;
if(top<50&&k&&i==top) top++;
//上一句要写在for循环里面,当第二次进入时,
//满足条件top+1之后还能再进行一次循环,若放在外面则不能,这样就会出错
}
p-=20;
}
while(p){
k=0;
for(i=1;i<=50&&i<=top;i++){
a[i]=(a[i]<<1)+k;
//注意位运算的优先级 :算术运算-> 移位运算->位运算 (逻辑运算)
k=a[i]/10000000000;
a[i]%=10000000000;
if(top<50&&k&&i==top) top++;//进位的充分必要条件
}
p--;
}
a[1]--;
for(i=50;i>=1;i--){
if(i%5==0&&i!=50) cout<<endl;
k=1000000000;//输出技巧
for(j=1;j<=10;j++){
cout<<(a[i]/k);
a[i]%=k;
k=k/10;
}
}
return 0;
}
注意下面这个写法是错误的,在左移20位时,后一位的进位也左移20位,导致结果变大
while(p>=20){
for(i=1;i<=50&&i<=top;i++){
a[i]=a[i]<<20;
a[i+1]+=a[i]/10000000000;
a[i]%=10000000000;
if(top<50&&a[top+1]&&i==top) top++;
}
p-=20;
}
普通进制转换做法(不能通过):
#include<bits/stdc++.h>
using namespace std;
int p,k=499;
int data[3100005];//根据需要更改
int jieguo[500];
int main(){
cin>>p;
memset(jieguo,0,sizeof(jieguo));
for(int i=0;i<p;i++) data[i]=1;
//转换
bool flag=true;
while(flag){
if(k<0) break;//根据题意剪枝
int num=0;
flag=false;
for(register int i=0;i<p;i++){
num=(num<<1)+data[i];//位运算最好加括号
data[i]=num/10;
num=num%10;
if(data[i]!=0&&flag==false) flag=true;//当data[]数组全是0时,flag=false,不再进入循环
}//for循环执行完之后num是本次短除之后的余数
jieguo[k--]=num;
}
printf("%d\n",(int)(log10(2)*p+1));//**直接算出位数
for(int i=0;i<10;i++){
for(int j=i*50;j<(i+1)*50;j++) cout<<jieguo[j];
cout<<endl;
}
return 0;
}