也是一道不错的数位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; }