本来是想刷数论题的,万万没想到的是,这道题题目是叫数论题,但其实它是道数位DP呢。
既然sum(i) 表示 i 的二进制表示中1的个数,而数据范围又很大,是1e15,暴力肯定是不行的。
但我们知道,肯定有很多数二进制的1的个数是一样的,考虑可不可以把问题转化成对于每一个k,找出二进制里有k个1的数有多少个。
我们发现把N换为2进制的话,也不过就是50位的样子。那么最多也才50个1,那么k从 1 for到 50,每个k找一遍,把个数存一下。
问题转化为:对于一个区间的数,找出二进制中有k个1的数有多少个----->数位dp。
#include<bits/stdc++.h> #define LL long long #define mod 10000007 using namespace std; LL read() { LL x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int num[52]; LL dp[52][52][52][3],f[52];//小心爆int int tot=0; LL dfs(int pos,int cnt,int goal,int lim) { if(!pos) return cnt==goal; if(~dp[pos][cnt][goal][lim])return dp[pos][cnt][goal][lim]; int maxx=lim?num[pos]:1; LL res=0; for(int i=0;i<=maxx;++i) res+=dfs(pos-1,cnt+(i==1),goal,lim&&(i==maxx)); return dp[pos][cnt][goal][lim]=res; } LL quick(LL a,LL x) { LL ans=1; while(x) { if(x&1)ans=ans*a%mod; a=a*a%mod;x>>=1; } return ans; } void solve(LL x) { while(x) { num[++tot]=x&1; x>>=1; } for(int i=2;i<=50;++i)memset(dp,-1,sizeof(dp)),f[i]=dfs(tot,0,i,1);//记得清空,每次要求的1的个数不同 LL ans=1; for(int i=2;i<=50;++i) if(f[i])ans=ans*quick(i,f[i])%mod; printf("%lld ",ans); } int main() { LL n=read(); solve(n); }