題目
3209: 花神的數論題
Time Limit: 10 Sec Memory Limit: 128 MBProblem Dexcription
設 sum(i) 表示 i 的二進制表示中 1 的個數。給出一個正整數 N ,花神要問你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘積。Input
一個正整數 N。
N≤10^15Output
一個數,答案模 10000007 的值。
Samples
Input | Output |
---|---|
571 | 2560979 |
3 | 2 |
4 | 2 |
8 | 24 |
9 | 48 |
1000000000000000 | 1030503 |
分析
- 可以把 n 看成二進制,分段來討論。
- 舉個例子模擬一遍即可意會:
n=100100(二進制下)
000001~011111 貢獻爲1C152C25∗3C35∗4C45∗5C15
100000 貢獻爲 1
100001~100011 貢獻爲1C12∗2C22
100100 貢獻爲 2
*有那麼一點數位 dp 用到的思想,對於([1~2^n)
這個區間裏所有數對答案的貢獻我們可以用上面的組合數方法求出來(分類 一個1,兩個1,三個1…),然後就可以把原來的 n 分成許多個這樣連續的“滿二次冪”區間(臨時造詞),注意一下底數要加上前面已經固定好的 1 的數量(我的程序中體現爲 cnt)。
* 可以再看看程序理解吧。
程序
#include <cstdio>
#define Ha 10000007
long long i,j,n,p,q,cnt,ret,ans=1ll,C[60][60];
long long ksm(long long x,long long y){
for (ret=1; y; y>>=1,x=(x*x)%Ha)
if (y&1) ret=(ret*x)%Ha;
return ret;
}
int main(){
for (C[0][0]=1,i=1; i<=55; i++)
for (j=0; j<=55; j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
scanf("%lld",&n);
for (cnt=0,p=53,q=1ll<<p; q; q>>=1,p--) if (n&q){
for (i=1; i<=p; i++)
ans=(ans*ksm(cnt+i,C[p][i]))%Ha;
ans=(ans*(++cnt))%Ha;
}
printf("%lld",ans);
}