題目鏈接:
題目背景
衆所周知,花神多年來憑藉無邊的神力狂虐各大 、、、、 當然也包括 啦。
題目
話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。 花神的題目是這樣的:設 表示 的二進制表示中 的個數。給出一個正整數 ,花神要問你 ,也就是 的乘積。
輸入
一個正整數 。
輸出
一個數,答案模 的值。
樣例輸入
3
樣例輸出
2
數據範圍
對於 的數據,
思路
這道題是數位,但是我用的是組合數。
我們從大到小枚舉位數,找到是的位數,那我們就可以讓它爲,後面的位數就可以隨便枚舉了。接着枚舉玩這一位之後,我們只要讓它一直是,那麼我們就可以繼續枚舉下面的,而不會重複也不會超上限來。
除了枚舉位數(設用枚舉),還要枚舉未確定的位數中的個數(設用枚舉),那麼我們設之前以及確定來的是的位數的數量是,那麼答案要乘上的值就是。就是整個數列中的個數,而就不用說了,組合數嘛,求出的個數爲的數有多少個嘛。
(至於怎麼搞,肯定就是預處理啊,因爲化爲二進制肯定不超過位,那我們只需要預處理出內的就可以了)
最後輸出的時候,有一點要注意,就是還要乘上計數器。因爲按上面那樣的話是不會算到本身的情況的,所以我們輸出的時候還要再乘上纔可以。
還有一個很重要的東西,因爲它不是素數,所以我們預處理組合數的時候就不可以用,而是要用它的歐拉函數值。
代碼
#include<cstdio>
#define mo 10000007
#define ll long long
using namespace std;
ll n, C[61][61], t, ans = 1;
ll qsm(ll a, ll b) {//快速冪
ll anan = 1;
while (b) {
if (b & 1) anan = (anan * a) % mo;
a = (a * a) % mo;
b >>= 1;
}
return anan;
}
int main() {
for (int i = 0; i <= 60; i++)//預處理組合數
C[i][0] = 1;
for (int i = 1; i <= 60; i++)
for (int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % 9988440;//因爲10000007不是素數,所以要用它的歐拉函數值phi(10000007)=9988440
scanf("%lld", &n);//讀入
for (int i = 60; i >= 0; i--) {//枚舉沒確定位數
if (!((n >> i) & 1)) continue;//當前位是0
for (int j = 0; j <= i; j++)//枚舉沒確定的1的個數
if (t + j)//的出來的至少有一個1
ans = (ans * qsm(t + j, C[i][j])) % mo;//乘起來
t++;//加計數器
}
printf("%lld", ans * t % mo);//輸出(要記得乘上計數器)
return 0;
}