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 }

 

posted @ 2018-08-26 22:16  bztMinamoto  阅读(225)  评论(0编辑  收藏  举报
Live2D