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
3
Sample Output
样例输出一
2
2
HINT
对于样例一,1*1*2=2;
数据范围与约定
对于 100% 的数据,N≤10^15
注意二进制中1的个数不会很多,所以我们可以把问题转化成求1到N中有多少数在二进制下有k个1。
发现这个条件后就是简单的数位DP了,设f[i][k]表示长度为i,含k个1且开头为0的数的个数,f[i][k]表示长度为i,含k个1且开头为1的数的个数。
最后快速幂起来得到答案。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; typedef long long ll; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline ll read() { ll x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int mod=10000007; ll f[60][60],g[60][60]; void init() { f[0][0]=g[0][1]=1; rep(len,0,58) rep(i,0,len+1) { g[len+1][i+1]+=f[len][i]+g[len][i]; f[len+1][i]+=f[len][i]+g[len][i]; } } int bit[60]; ll ans[60]; ll solve(ll x) { int len=0,cnt=0; while(x) bit[len++]=x&1,x>>=1; rep(i,0,len-2) rep(j,0,i+1) ans[j]+=g[i][j]; dwn(i,len-1,0) { if(i!=len-1&&bit[i]) rep(k,0,i+1) ans[k+cnt]+=f[i][k]; cnt+=bit[i]; } } ll pow(int a,ll n) { if(!n) return 1; ll ans=pow(a,n>>1);(ans*=ans)%=mod; if(n&1) (ans*=a)%=mod; return ans; } int main() { init();solve(read()+1);ll res=1; rep(i,1,59) (res*=pow(i,ans[i]))%=mod; printf("%lld\n",res); return 0; }