高精快速幂+压位

142 篇文章 0 订阅
73 篇文章 0 订阅

**题目:**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;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值