P1045 [NOIP2003 普及組] 麥森數極簡解法解讀

kaitists發表於2024-08-17

原始碼如下 (這個精妙絕倫的演算法不是我發現的,而是取自原題解中的某個大佬,在經過一頓學習正常題解後看到,頓覺豁然開朗,原貼: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位數進行單獨求解,而位數可以直接透過公式計算得出:(p
log10(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次方,超過上限並不多,所以這個演算法其實是一個運算效能換時間的方法,如果數字繼續加大,這個方法可能並不適用,這裡用這個演算法的原因是十分精妙而且有使用價值,順便也複習了<<=的用法,用來一次運算多次冪十分可行。

相關文章