bzoj 3209 花神的数论题
题目大意:
设 sum(i)表示i的二进制表示中1的个数
给出一个正整数N,求是sum(1)—sum(N)的乘积
思路:
可以想到对sum的值求有多少个 然后快速幂
枚举sum的值 使用数位dp
每遇到一位1 则可以求出小于这位1所有的对于这个sum的组合数
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 200100 12 #define MOD 10000007 13 using namespace std; 14 inline ll read() 15 { 16 ll x=0,f=1;char ch=getchar(); 17 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 18 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 19 return x*f; 20 } 21 ll n,c[80][80],g[80],cnt,ans; 22 ll calc(int x) 23 { 24 ll res=0; 25 for(int i=cnt;i&&x>=0&&i-1>=x;i--) 26 if(g[i]) res+=c[i-1][x--]; 27 return res; 28 } 29 ll q_pow(ll bas,ll t) 30 { 31 ll res=1; 32 for(;t;t>>=1,(bas*=bas)%=MOD) 33 if(t&1) (res*=bas)%=MOD; 34 return res; 35 } 36 int main() 37 { 38 n=read()+1,ans=1,c[0][0]=1; 39 for(int i=1;i<=60;i++) 40 for(int j=c[i][0]=1;j<=i;j++) c[i][j]=c[i-1][j-1]+c[i-1][j]; 41 while(n) g[++cnt]=n&1,n>>=1; 42 for(int i=1;i<=cnt;i++) 43 (ans*=q_pow(i,calc(i)))%=MOD; 44 printf("%lld\n",ans); 45 }