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 }