【花神的數論題】
阿新 • • 發佈:2019-01-01
定義\(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; }