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;
}
相關文章
相關標籤/搜索