源代码如下 (这个精妙绝伦的算法不是我发现的,而是取自原题解中的某个大佬,在经过一顿学习正常题解后看到,顿觉豁然开朗,原贴:https://www.luogu.com.cn/article/c3u874kg )
include
include
include
using namespace std;
long long a[501]={1};
int main()
{
int p;
cin>>p;
cout<<(int)(plog10(2))+1<<endl;
for(;p>0;p-=60)
{
long long f=0;
for(int i=0;i<500;i++)
{
if(p>60)a[i]<<=60;
else a[i]<<=p;
a[i]+=f;
f=a[i]/10;
a[i]%=10;
}
}
a[0]--;
for(int i=499;i>=0;i--)
{
putchar(a[i]+'0');
if(i%50==0)putchar('\n');
}
return 0;
}
此题不能纯模拟,会爆内存,对于题目要求的求最后500位数进行单独求解,而位数可以直接通过公式计算得出:(plog10(2))+1
解释:10k有k+1位,而k=lg(10k)。所以2p的位数是lg(2p)+1=p*lg(2)
正常的解法是用高精快速幂求解,但是本体的主体中使用一个十分精简的算法:
下面解决500位求值的问题:这里还是开一个a[501]的long long数组,然后还是用每个元素表示1位数,没错,是1位数,这样时间够而且代码简单。每次乘一轮不要乘2,乘2^60(9乘以2的60次方刚好不会溢出),记得把p多减掉59就行了。
关于主体的解释:
for(;p>0;p-=60)
{
long long f=0;
for(int i=0;i<500;i++)
{
if(p>60)a[i]<<=60;
else a[i]<<=p;
a[i]+=f;
f=a[i]/10;
a[i]%=10;
}
}
可以简单看做仅计算前500位的纯模拟60次的快速运算,其他次数也可以
f就是纯模拟中的进位,只不过这里也是一次计算了60次
<<= n 是指将数转化为二进制后整体向左移n位后赋值,在正常的纯模拟中也可以使用,而且似乎十分方便。
纯模拟500位的运算是否会爆内存,经过计算复杂度为10的10次方,超过上限并不多,所以这个算法其实是一个运算性能换时间的方法,如果数字继续加大,这个方法可能并不适用,这里用这个算法的原因是十分精妙而且有使用价值,顺便也复习了<<=的用法,用来一次运算多次幂十分可行。