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 }
View Code

 

posted @ 2018-10-29 09:21  jack_yyc  阅读(126)  评论(0编辑  收藏  举报