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;
}