二进制数位dp,就是把原本的数字转化成二进制而以,原来是10进制,现在是二进制来做,没有想像的那么难
不知到自己怎么相出来的。。。感觉,如果没有一个明确的思路,就算做出来了,也并不能锻炼自己的能力,因为我现在需要训练的是做题的思维方法啊!
sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,求sum(1)—sum(N) 的乘积。
首先,这道题直接做,感觉无从下手,那么我就想,怎么来转换一下,求1~n中每个数的一的个数总相乘之积,首先感觉到,每个数都会有唯一对应的1的个数,且一的个数的取值只有最多60,因为n最大 10^15, 那么我就想,如果枚举1的个数k,计算有多少个数含有k个1,(因为数位dp就是来做,有多少满足的数,且不关注数的大小)这样就转化为数位dp的模型了
另外,发现含有k个1的数个数可能非常多,快速幂搞一搞啦,
不过快速幂要注意超long long 的情况!!,因为在很多题mod比较大,mod<根号2^31,平方之后就有可能超int!!!!!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 typedef long long int ll; 9 const int MAXN=60+5; 10 const ll mod=10000007; 11 12 ll n,Ans; 13 ll c[MAXN][MAXN]; 14 int l,wei[MAXN]; 15 void pre() 16 { 17 for (int i=0;i<=60;++i) 18 c[i][0]=1; 19 for(int i=1;i<=60;i++) 20 for(int j=1;j<=i;++j) 21 c[i][j]=c[i-1][j-1]+c[i-1][j];//c[i][j]表示i位,j个1。 22 scanf("%lld",&n); 23 l=0,n+=1;//只能计算小于n的个数,所以要加1 24 while(n) 25 { 26 wei[++l]=n&1; 27 n>>=1; 28 }//将n转换为2进制。 29 } 30 ll Solve(int x)//解决有x个1的方案数 31 { 32 ll sum=0; 33 for (int i=l;i>=1;--i) 34 { 35 if(wei[i]==1) 36 { 37 sum+=c[i-1][x]; 38 x--; 39 } 40 if(x<0) break; 41 } 42 return sum; 43 } 44 ll Pow(ll a, ll b){ 45 ll tot=1; 46 a%=mod; 47 while(b) 48 { 49 if(b&1) 50 { 51 tot=a*tot%mod; 52 tot%=mod; 53 } 54 b>>=1;a*=a;a%=mod; 55 } 56 return tot; 57 } 58 int main() 59 { 60 pre(); 61 Ans=1ll; 62 for(int i=1;i<=l;++i) 63 Ans=Ans*Pow(i,Solve(i))%mod; 64 printf("%lld ",Ans); 65 return 0; 66 }