1. 程式人生 > >【花神的數論題】

【花神的數論題】

定義\(sum(i)\)表示\(i\)在二進位制下\(1\)的個數

\(\prod_{i=1}^{n}sum(i)\)

暴力非常\(sb\)顯然可以隨便寫,但是顯然也是會\(T\)

於是我們換個思路

我們設\(tot\)表示\(sum(i)=x\)\(i\)有多少個,於是答案就是\(x^{tot}\)

我們列舉\(x\)就行了,\(x\)顯然不會很大,也就是\(log_2{n}\)

之後就可以開始數位\(dp\)

我們設\(dp[i][0/1][j]\)表示最高位填到第\(i\)位,其中最高位填\(0/1\),一共填了\(j\)個1一共有多少個數

方程很顯然,就是往最高位上填\(0/1\)

填0的話

\[dp[i+1][0][j]+=dp[i][1][j]+dp[i][0][j]\]

填1的話

\[dp[i+1][1][j+1]+=dp[i][0][j]+dp[i][1][j]\]

之後我們按照數位\(dp\)的套路來做就行了

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long 
#define re register
#define maxn 55
const LL mod=10000007;
const LL P=9988440;
LL n;
int a[maxn];
LL dp[maxn][2][maxn];
inline LL quick(LL a,LL b)
{
    LL S=1;
    while(b)
    {
        if(b&1) S=S*a%mod;
        b>>=1ll;
        a=a*a%mod;
    }
    return S;
}
inline LL sum(LL x)
{
    LL cnt=0;
    while(x)
    {
        if(x&1ll) cnt++;
        x>>=1ll;
    }
    return cnt;
}
inline LL slove(LL x)
{
    int num=0;
    while(x)
    {
        ++num;
        if(x&1ll) a[num]=1;
        x>>=1ll;
    }//分解數位
    dp[1][1][1]=1;
    dp[1][0][0]=1;
    for(re int i=1;i<num;i++)
        for(re int j=0;j<=i;j++)
        {
            dp[i+1][1][j+1]=(dp[i+1][1][j+1]+dp[i][0][j]+dp[i][1][j]);
            dp[i+1][0][j]=(dp[i+1][0][j]+dp[i][0][j]+dp[i][1][j]);
        }//dp的過程
    LL ans=1;
    for(re int T=1;T<=num;T++)//列舉T求出有多少i,sum(i)=T
    {
        for(re int i=1;i<num;i++)
            ans=(ans*quick(T,dp[i][1][T])%mod)%mod;
        //先列舉位數比n要小的
        int k=T;
        if(a[num]) k--;
        for(re int i=num-1;i>=1;i--)//之後我們卡前面的[i+1,num]為完全相等,讓第i位小於n的第i位,我們就可以讓後面的位數隨便填了
        {
            for(re int j=0;j<a[i];j++)
                ans=(ans*quick(T,dp[i][j][k])%mod)%mod;
            if(a[i]) k--;//別忘了保證前面的數位相等,要減去前面1的個數
            if(k<0) break;
        }
    }
    return ans;
    //由於一直在卡上界,但最後也沒有卡到等於n的情況,於是n的暴力分解數位處理一下就好了
}
int main()
{
    scanf("%lld",&n);
    std::cout<<sum(n)*slove(n)%mod;
    return 0;
}