BZOJ 3209: 花神的数论题 (数位dp)
题目描述
背景
众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。
描述
话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。
花神的题目是这样的
设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。
输入
一个正整数 N。
输出
一个数,答案模 10000007 的值。
样例输入
样例输入
3
样例输出
样例输出
2
对于 100% 的数据,N≤10^15
题解
数位dp,抄的大佬的题解
然而实在不是很看得懂……
下面$g[i]$表示$1$恰好有$i$个时候的方案数
然后为啥能这样转移呢?我想了想,大概是因为它每一次遇到$1$时,就默认这一位可以放,那么每一个$g[i]$都能转移到$g[i+1]$,然后后面还有$1$那么考虑后面的,这样防止超出界限
1 //minamoto 2 #include<cstdio> 3 #define ll long long 4 const int mod=1e7+7; 5 ll ans=1,n,c,g[55]; 6 ll qpow(ll x,ll y){ 7 ll res=1; 8 while(y){ 9 if(y&1) res=res*x%mod; 10 y>>=1,x=x*x%mod; 11 } 12 return res; 13 } 14 int main(){ 15 scanf("%lld",&n); 16 for(int j=49;~j;--j){ 17 for(int i=49;i;--i) g[i]+=g[i-1]; 18 if(n>>j&1) ++g[c++]; 19 } 20 ++g[c]; 21 for(int i=1;i<=49;++i) ans=ans*qpow(i,g[i])%mod; 22 printf("%lld\n",ans); 23 return 0; 24 }
深深地明白自己的弱小