bzoj3209: 花神的数论题(数位DP)

3209: 花神的数论题

题目:传送门 

题解:

   要注意到有可能同一个sum有多个的情况

   定义一个 f[i][j] 和 g[i][j] 表示:

   二进制位数位i,最高位为0,共有j个1  &&  二进制位数位i,最高位为1,共有j个1

   转移很简单就不说了。

   

   这样子就可以瞎枚举一下n的二进制位,如果为1就快速幂计算一下答案(细节很多,具体看代码)

奇丑无比的代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 typedef long long LL;
 8 const LL mod=10000007;
 9 LL f[110][110],g[110][110];//共i位,最高位为 0/1 ,共j个1 
10 LL n;
11 LL p_m(LL a,LL b)
12 {
13     LL ans=1;
14     while(b!=0)
15     {
16         if(b%2==1)ans=(ans*a)%mod;
17         b/=2;a=(a*a)%mod;
18     }
19     return ans;
20 }
21 int main()
22 {
23     scanf("%lld",&n);LL len=0;
24     LL x=n;while(x){x/=2;len++;}
25     f[1][0]=1;g[1][1]=1;
26     for(int i=2;i<=len;i++)
27         for(int j=0;j<=i;j++)
28         {
29             f[i][j]=f[i-1][j]+g[i-1][j];
30             if(j)g[i][j]=f[i-1][j-1]+g[i-1][j-1];
31         }
32     LL ans=1,sum=0;
33     for(int k=len;k>=1;k--)
34     {
35         if(1LL<<(k-1)&n)
36         {
37             for(int i=1;i<=k;i++)ans=(ans*p_m(i+sum,f[k][i]))%mod;
38             if(sum)ans=(ans*sum)%mod;
39             sum++;
40         }
41     }
42     printf("%lld\n",(ans*sum)%mod);
43     return 0;
44 }
posted @ 2018-02-28 12:50  CHerish_OI  阅读(152)  评论(0编辑  收藏  举报