Luogu P4317 花神的数论题

也是一道不错的数位DP,考虑先转成二进制后再作ui

转化一下问题,考虑统计出\([1,n]\)中在二进制下有\(i\)\(1\)的方案数\(cnt_i\),那么答案显然就是\(\prod i^{cnt_i}\)spa

而后咱们仍是先预处理一个东西\(s_{i,j}\),表示在二进制下前\(i\)位中填上\(j\)\(1\)的方案数,则有转移:code

\(s_{i,j}=s_{i-1,j}+s_{i-1,j-1}(i>1)\),同时有\(s_{i,0}=1\)it

这转移很简单吧,就是考虑这一位填上\(0/1\)io

观察一下发现其实这就是个杨辉三角,不过好像并无什么用。class

接下来枚举有\(i\)\(1\)的状况,那么从高位填到低位,对于每一位上的\(1\),我后面怎么填都是知足要求的二进制

所以此时的\(cnt_i+=s_{l,k}\)\(l\)表示后面还有多少位(比它低的位),\(k\)表示以前(包括如今)已经出现多少个\(1\),最后直接快速幂计算一下就行了。统计

注意到这样只能处理小于\(n\)的数的状况(通常不少二进制下的数位DP都有这个通病),因此咱们直接把\(n\)加一便可。di

CODEwhile

#include<cstdio>
using namespace std;
const long long N=65,mod=10000007;
long long n,s[N][N],ans=1LL,cnt,bit[N];
inline void resolve(long long x)
{
    while (x) bit[++cnt]=x&1,x>>=1;
}
inline long long solve(long long x)
{
    register long long i; long long tot=0;
    for (i=cnt;i>=1&&~x;--i)
    if (bit[i]) tot+=s[i-1][x--];
    return tot;
}
inline long long quick_pow(long long x,long long p)
{
    long long tot=1;
    while (p)
    {
        if (p&1) tot=tot*x%mod;
        x=x*x%mod; p>>=1;
    }
    return tot;
}
int main()
{
    register long long i,j; scanf("%lld",&n); resolve(++n);
    for (s[0][0]=1,i=1;i<=cnt;++i)
    for (j=0;j<=i;++j)
    s[i][j]=j?s[i-1][j]+s[i-1][j-1]:s[i-1][j];
    for (i=1;i<=cnt;++i)
    ans=ans*quick_pow(i,solve(i))%mod;
    return printf("%lld",ans),0;
}
相关文章
相关标签/搜索