bzoj 3209 花神的数论题——二进制下的数位dp

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3209

可以枚举 “1的个数是...的数有多少个” ,然后就是用组合数算在多少位里选几个1。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e7+7,N=70;//(1e7+7)%941==0
ll n,dg[N];
int jc[N],jcn[N],cnt,ans=1;
int pw(int x,int k)
{
    int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
void exgcd(int a,int b,int &x,int &y)
{
    if(!b){x=1;y=0;return;}
    exgcd(b,a%b,y,x);y-=a/b*x;
}
void init()
{
    jc[0]=1;
    for(int i=1;i<=62;i++)jc[i]=(ll)jc[i-1]*i%mod;
    int y;exgcd(jc[62],mod,jcn[62],y);
    for(int i=61;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;
}
int C(int n,int m)
{
    if(m>n)return 0;if(!m)return 1;
    return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod;
}
int main()
{
    init();
    scanf("%lld",&n);
    ll m=n;int p0=0;
    while(m)
    {
        ll k=(m&-m);
        for(;(1ll<<p0)!=k;p0++);
        dg[++cnt]=p0;m-=(m&-m);
    }
    for(int i=1;i<=dg[cnt]+1;i++)
        for(int j=cnt,k=0;j>=0&&k<=i;j--,k++)
            ans=(ll)ans*pw(i,C(dg[j],i-k))%mod;//模数不是质数,不能对指数取模!!! 
    printf("%d\n",ans);
    return 0;
}
View Code

模数有毒吧怎么不是质数啊!这样都没法对指数取模了!

然后得知是数位dp。同样是枚举 “1的个数是...的数有多少个” ,但因为不是组合数,所以long long就行啦!

dp[ i ][ j ]表示第 i 位是0,后面从0..00到1..11中有多少个数有 j 个1。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=62,mod=1e7+7;
ll n,dp[N+5][N+5];
int m,dg[N+5],cnt,ans=1;
void init()
{
    dp[1][0]=1;
    for(int i=2;i<=m;i++)
        for(int j=0;j<=i;j++)
            dp[i][j]+=dp[i-1][j]+dp[i-1][j-1];
}
int pw(int x,ll k)
{
    if(!x)return 1;
    int ret=1;while(k){if(k&1ll)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1ll;}return ret;
}
int main()
{
    scanf("%lld",&n);
    for(m=0;m<=N&&(1ll<<m)<=n;m++);
    init();
    ll c=n;int p0=0;
    while(c)
    {
        ll k=(c&-c);
        for(;(1ll<<p0)!=k;p0++);
        dg[++cnt]=p0+1;c-=k;
    }
    for(int i=cnt,k=0;i>=0;i--,k++)
    {
        if(!i){ans=(ll)ans*k%mod;break;}
        for(int j=0;j<=dg[i]-1;j++)
            ans=(ll)ans*pw(j+k,dp[dg[i]][j])%mod;
    }
    printf("%d\n",ans);
    return 0;
}

 

posted on 2018-08-01 09:56  Narh  阅读(209)  评论(0编辑  收藏  举报

导航