题目链接
https://www.dotcpp.com/oj/problem1607.html
题目大意
计算2^p-1位数,及低500位数,输出。
解题思路
2的p次方,快速幂吧!
只不过快速幂中的乘,得换成高精度乘法了。
先看看快速幂的板子吧
还不知道啥是快速幂看看这个吧
ll ksm(ll x,ll t){//快速幂
ll res=1;
while(t>0){
if(t&1LL) res=res*x%mod;
t>>=1;
x=x*x%mod;
}
return res;
}类比一下,乘换成高精度,把取模操作去掉。
我的AC代码
直接对着代码讲吧,方便理解。
先整体看看,不看细节。
先看主函数高精度快速幂求出2^p,所谓高精度快速幂不过是把单精度加减乘除换成高精度的;
res--,求出的res的最低位要-1,求出2^p-1;
ceil,天花板的意思,就是取大于某数的最小整数。相对的,floor,地板,取小于某数的最大整数。重点是这个数学公式(根本没听说过),ceil(幂*log10(底数)) = 底数^幂 的位数;
输出。
函数讲解:
mul函数:高精度相乘,但是针对本题而言,因为我们只取低500位,因此,没必要用字符串存再转化,直接用数组存就行。这样一来,循环的终点都是500了,我们给全局数据res(即存最终答案的数组)赋值的时候,也是直接按着c的顺序赋值的,没必要反过来,反过来之后,再次进入相乘的时候,又要反过来计算,计算完了又反过来,可见,这是多此一举,所以,用res的低索引存低位。
x_mul函数:求x的平方,依然是只取低500位,与mul函数代码类似。
//我的代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int res[N]={0,1},x[N]={0,2};//初始化不能忘!!忘掉爆炸!!
int c[N];
void mul(int* a,int* b){
memset(c,0,sizeof c);//勿忘
for(int i=1;i<=500;i++)//不管结果有多少位,我们只管最低500位,且低位的数值是不受高位影响的,但是高位的话会受到低位影响
for(int j=1;j<=500;j++){//高精度乘法板子
c[i+j-1]+=a[i]*b[j];
c[i+j]+=c[i+j-1]/10;
c[i+j-1]%=10;
}
for(int i=1;i<=500;i++)
res[i]=c[i];//res与c数组相同的存储方式,小索引存低位
}
void x_mul(){
memset(c,0,sizeof c);//勿忘
for(int i=1;i<=500;i++)
for(int j=1;j<=500;j++){
c[i+j-1]+=x[i]*x[j];
c[i+j]+=c[i+j-1]/10;
c[i+j-1]%=10;
}
for(int i=1;i<=500;i++)
x[i]=c[i];
}
int main(){
int p;
cin>>p;
int tmp=p;
//快速幂变形而已
while(tmp>0){
if(tmp&1) mul(res,x);
tmp>>=1;
x_mul();
}
res[1]--;//求出的res的最低位要-1,因为要求2^p-1
//为什么直接最低位-1就行,万一个位数是0呢?
//其实,根本不存在个位为0的情况,至少也是2,因为2^1=2,无论乘多少个2,个位显然都不会变成0吧,除非乘0,所以你可以放心地进行-1;
cout<<ceil(p*log10(2));//ceil函数+数学公式
for(int i=500;i>=1;i--){//输出
if((i)%50==0) cout<<endl;
cout<<res[i];
}
} 大佬的AC代码
本质没变,还是高精度快速幂。
大佬有三个函数,pow_2函数:高精度平方函数,mul_2函数:高精度乘2函数,get_res函数:快速幂函数。
get_res函数:
递归调用自己,若幂/2不为1,继续递归调用自己,参数为n/2;
进行平方操作;
若x为奇数,将结果进行乘2操作。
(不是很会讲,你可以举几个例子试试,你可以理解为一个木棒,不停地截取一半,要是能正好分一半,就平方,没法正好分一半,就平方后再乘上2)
pow_2函数:
类板子,求平方,取低500位。
mul_2函数:
类板子,*2操作,取低500位。
#include<bits/stdc++.h>
using namespace std;
const int N=1200;
int res[N];
int p;
void pow_2(){
int c[N];
memset(c,0,sizeof c);
for(int i=1;i<=500;i++)
for(int j=1;j<=500;j++)
c[i+j-1]+=res[i]*res[j];
for(int i=1;i<=500;i++){
c[i+1]+=c[i]/10;
c[i]%=10;
}
for(int i=1;i<=500;i++)
res[i]=c[i];
}
void mul_2(){
int c[N];
memset(c,0,sizeof c);
for(int i=1;i<=500;i++)
res[i]*=2;
for(int i=1;i<=500;i++){
res[i+1]+=res[i]/10;
res[i]%=10;
}
}
void get_res(int x){
if(x/2!=1) get_res(x/2);
pow_2();
if(x%2==1) mul_2();
}
int main(){
cin>>p;
cout<<ceil(p*log10(2));
res[1]=2;
get_res(p);
res[1]--;
for(int i=500;i>=1;i--){
if(i%50==0) cout<<endl;
cout<<res[i];
}
}二者区别
我的代码:
大佬代码:
“正确”后的两个数依次是“内存”和“耗时”。
总结
尽管知道是高精度快速幂,但是还是没实现出来,最后还是仿照大佬的代码去实现了一遍。
重点记忆:
ceil(幂*log10(底数)) 为底数^幂的位数;
不同的快速幂板子。

京公网安备 11010502036488号