P4317 花神的數論題,關於luogu題解粉兔做法的理解
阿新 • • 發佈:2020-11-03
題意
設 \(\text{sum}(i)\) 表示 \(i\) 的二進位制表示中 \(1\) 的個數。給出一個正整數 \(N\) ,求 \(\prod_{i=1}^{N}\text{sum}(i)\) 。
思路
換一種角度看這個乘積,會發現就相當於統計出 \(1\sim N\) 中 1 的個數為 \(k\) 的數量 \(cnt_k\) ,然後 \(\prod k^{cnt_k}\) 即可。
(怎麼那麼水啊,這都什麼垃圾紫題,題白挑了)為了讓這道題更有價值,程式碼實現非常的神仙。Orz粉兔。
粉兔的程式碼看了很久才理解……luogu上至今沒有看到公開的詳解。
這裡註釋的是我認為正確的理解,若有差錯還請指正。
程式碼
#include <cstdio> #define ll long long const ll mod=1e7+7; ll n,ans=1,cnt,f[50]; ll power( ll a,ll b ) { ll res=1; for ( ; b; b>>=1,a=a*a%mod ) if ( b&1 ) res=res*a%mod; return res; } int main() { scanf( "%lld",&n ); cnt=0; f[0]=0; for ( int len=49; ~len; --len ) { for ( int i=49; i; --i ) f[i]+=f[i-1]; if ( n>>len&1 ) f[cnt]++,cnt++; //cnt記錄的是除了現在這一位,之前有的1的個數,f[cnt]++表示,這一位的1產生了一種使得前面的1全部能取到的方案。 } f[cnt]++; //加上本身 //之前一直想不明白,如果這樣列舉,為什麼能直接從49開始。 //一開始的想法是預支最高位的1,這樣當前每次加一位就能取1,對應 f[i-1] 到 f[i] 的轉移 //但是這樣有個問題,就是最高位沒有1了怎麼辦,這樣預支無效,答案就會偏大 //後來發現,關鍵在外層迴圈。當位數大於二進位制下n的位數的時候,f始終為0,最後一句if 不會執行,也就不會出現上述問題。 //一旦開始累加出現了值,那麼一定就是有高位可以預支了。否則 if 中的等號不會成立。 for ( int i=1; i<=49; ++i ) ans=ans*power( i,f[i] )%mod; printf( "%lld",ans ); return 0; }