1. 程式人生 > >bzoj3209-花神的數論題

bzoj3209-花神的數論題

Description

眾所周知,花神多年來憑藉無邊的神力狂虐各大 OJ、OI、CF、TC …… 當然也包括 CH 啦。

話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。
花神的題目是這樣的
設 sum(i) 表示 i 的二進位制表示中 1 的個數。給出一個正整數 N ,花神要問你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘積。

Input

一個正整數 N。

Output

一個數,答案模 10000007 的值。

Sample Input

樣例輸入一

3

Sample Output

樣例輸出一

2

HINT

對於樣例一,112=2;

資料範圍與約定

對於 100% 的資料,N≤10^15.

Solution

簡單的數位dp.

列舉1的個數, 然後dp求出數的個數.

注意指數不能取模!
由於指數意義是數的個數, 顯然不會超過long long, 直接快速冪即可.

Code

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const ll nsz=60,nmod=1e7+7;
ll n;
ll dig[nsz],pd=0;
ll dp[nsz][nsz];
ll qp(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%nmod;
        a=a*a%nmod,b>>=1;
    }
    return res;
}
ll sol1(ll cnt){
    ll res=0;
    repdo(i,pd,1){
        if(dig[i])res=res+dp[i-1][cnt],--cnt;
        if(cnt<0)break;
    }
    if(cnt==0)++res;
    return res;
}
ll sol(){
    while(n)dig[++pd]=n&1,n>>=1;
    rep(i,0,pd)dp[i][0]=1;
    rep(i,1,pd)rep(j,1,i)dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
//  rep(i,0,pd){rep(j,0,pd)cout<<dp[i][j]<<' ';cout<<'\n';}
    ll ans=1;
    rep(i,1,pd){
        ans=(ans*qp(i,sol1(i)))%nmod;
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n;
    cout<<sol()<<'\n';
    return 0;
}