bzoj3209: 花神的数论题

来源:互联网 发布:负离子吹风机 知乎 编辑:程序博客网 时间:2024/05/22 16:44

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3209

题解

  这种题。。。没有1A是耻辱,一开始模数打错了,以为是109+7,其实题目里是107+7
  f[i][j]、g[i][j]分别表示在无、有限制的情况下i位数包含j个1的方案数。(允许前导零)
  不难发现log2101550,因此题目中sum(i)不超过50,所以可以枚举sum(i)的值,题目就变成求[1,N]里有多少个数的二进制表示中恰好有x个1。
  最后快速幂即可。

代码

//数位dp#include <cstdio>#include <algorithm>#define maxn 60#define ll long long#define p 10000007using namespace std;ll f[maxn][maxn], g[maxn][maxn], num[maxn], N;void init(ll x){for(;x;x>>=1)num[++N]=x bitand 1;}void dp(){    ll i, j;    for(i=0;i<=N;i++)f[i][0]=g[i][0]=1;    for(i=1;i<=N;i++)for(j=1;j<=N;j++)f[i][j]=f[i-1][j-1]+f[i-1][j];    g[0][0]=1;    for(i=1;i<=N;i++)for(j=1;j<=N;j++)        if(num[i]==1)g[i][j]=f[i-1][j]+g[i-1][j-1];        else g[i][j]=g[i-1][j];}ll count(ll x){    ll ans=g[N-1][x-1], i;    for(i=N-1;i>=1;i--)ans+=f[i-1][x-1];    return ans;}ll pow(ll a, ll b){    ll t, ans;    for(ans=1,t=a;b;t=(t*t)%p,b>>=1)if(b&1)ans=(ans*t)%p;    return ans;}int main(){    ll n, i, ans=1;    scanf("%lld",&n);    init(n);    dp();    for(i=1;i<=N;i++)ans=(ans*pow(i,count(i)))%p;    printf("%lld",ans);    return 0;}
0 0
原创粉丝点击