题目连接
- 该题是luogu试炼场的2-11:T2
- 参考1:高精度*高精度(稍后更新)
- 参考2:快速幂
题目大意
- 输入n,求 2n-1 的值;
- 输出有要求1:输出数位;
- 输出有要求2:后500位,分10行输出;
题目分析
- n的范围是(1000,3100000),还给出了极值的长度有90W+位:
- 朴素思维1:高精度*低精度,暴力拿50分soeasy!
- 正派思路2:数学计算+快速幂 满分
- 骨骼清奇的思路3:强行压位暴力算
朴素思维:高精度*低精度
- 暴力循环 n 次,2n-1应该能拿部分分;
- 提交发现拿了 50分,仁慈的数据~~
代码:
//luogu1045:麦森数1:50分
//高精度*低精度
//暴力拿部分分
#include <bits/stdc++.h>
using namespace std;
struct nod{int a[10000005],len;}a;
int n;
void cf()
{
//乘法
for(int i=1;i<=a.len;i++) a.a[i]*=2;
//进位
for(int i=1;i<=a.len;i++)
{
if(a.a[i]>=10)
{
a.a[i+1]+=a.a[i]/10;
a.a[i]%=10;
if(i==a.len) a.len++;
}
}
}
int main()
{
scanf("%d",&n);
a.len=1;
a.a[1]=1;
for(int i=1;i<=n;i++)
{
cf();//*2的计算
}
//个位 -1
a.a[1]-=1;
//借位
for(int i=1;i<=a.len;i++)
{
if(a.a[i]<0)
{
a.a[i]=0;
a.a[i+1]--;
}
else break;
}
printf("%d\n",a.len);//输出位数
n=10;
while(n--)
{
for(int i=50;i>=1;i--) printf("%d",a.a[n*50+i]);
printf("\n");
}
return 0;
}
解题思路:数学计算+快速幂
- 3w*90w的运算,暴力肯定是过不了的;
- 答案只要求输出500位,显然可能500位以上的,不需要具体运算;
- 就可以分两步走了:
-
用某些(数学)方法算出答案的数位长度:2n 与数位的关系?
- 1 我们知道10x 的数位就是 x+1;
- 2 设2n =10x
- 3 x = log10 ( 2n )
- 4 因为有公式: logn(a*b)=logn(a)+logn(b)
- 5 由3 联立: x = log10 ( 2 * 2 * 2 * 2 * 2 * …* 2)
- 6 由 4,5联立:x = log10(2)+log10(2)+log10(2)+ …+log10(2)
- 7 所以: x = n * log10(2) ;
-
进行500位的快速幂运算;
代码2:
- 计算数位+快速幂优化
//luogu1045:麦森数
//1:数学计算数位
//2:高精度*高精度+快速幂
#include <bits/stdc++.h>
using namespace std;
struct nod{int a[1005],len; nod(){memset(a,0,sizeof(a));}}a,b;
int n;
nod cf(nod x,nod y)//最多只做 500 位的运算
{
//初始化
nod z;
if(x.len>500) x.len=500;
if(y.len>500) y.len=500;
z.len=x.len+y.len-1;
//乘法
for(int i=1;i<=x.len;i++)
{
for(int j=1;j<=y.len;j++)
{
z.a[i+j-1]+=x.a[i]*y.a[j];
}
}
//进位
for(int i=1;i<=z.len;i++)
{
if(z.a[i]>=10)
{
z.a[i+1]+=z.a[i]/10;
z.a[i]%=10;
if(i==z.len&&z.len<=500) z.len++;
}
}
return z;
}
int main()
{
scanf("%d",&n);
printf("%d\n",int(log10(2)*n+1));//数学算位数
a.len=1; a.a[1]=1;//存答案
b.len=1; b.a[1]=2;//寄存过程值
while(n>0)//快速幂
{
if(n%2>0) a=cf(a,b);//幂是奇数:2^3=2*(2^2)
b=cf(b,b);//幂是偶数:2^8=(2^2)^4
n/=2;
}
//个位 -1
a.a[1]-=1;
for(int i=1;i<=a.len;i++)//借位
{
if(a.a[i]<0)
{
a.a[i]=0;
a.a[i+1]--;
}
else break;
}
//输出
n=10;
while(n--)
{
for(int i=50;i>=1;i--) printf("%d",a.a[n*50+i]);
printf("\n");
}
return 0;
}