bzoj 3209: 花神的数论题【数位dp】

参考:https://blog.csdn.net/sunshinezff/article/details/51049132
非典型数位dp
首先预处理,设f[i][j]为以0开头的i位数中1的个数为j的数的数量,g[i][j]为以1开头的i位数中1的个数为j的数的数量;转移是 f[i][j]=f[i-1][j]+g[i-1][j],g[i][j]=f[i-1][j-1]+g[i-1][j-1]
然后做数位dp,对于n在二进制下为1的位统计这样的1的个数出现过几次,然后快速幂即可

#include<iostream>
#include<cstdio>
using namespace std;
const int N=65,mod=10000007;
long long n,f[N][N],g[N][N];
long long ksm(long long a,long long b)
{
    long long r=1ll;
    while(b)
    {
        if(b&1)
            r=r*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return r;
}
int main()
{
    scanf("%lld",&n);
    f[1][0]=1,g[1][1]=1;
    for(int i=2;i<=60;i++)
        for(int j=0;j<=i;j++)
        {
            f[i][j]=f[i-1][j]+g[i-1][j];
            if(j>0)
                g[i][j]=f[i-1][j-1]+g[i-1][j-1];
        }
    long long t=0,c=0,ans=1;
    for(t=0;(1ll<<t)<=n;t++);
    for(;t;t--)
        if(1ll<<(t-1)&n)
        {
            for(int i=1;i<=t;i++)
                ans=ans*ksm(i+c,f[t][i])%mod;
            if(c)
                ans=ans*c%mod;
            c++;
        }
    printf("%lld\n",ans*c%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/9392152.html