花神的數論題 數位dp
阿新 • • 發佈:2020-08-15
題目描述
設$sum(i)$表示$i$的二進位制表示中$1$的個數。
給出一個正整數$N$,花神要問你:$\prod_{i=1}^nsum(i)$。
資料範圍
對於$100%$的資料,$N\leq10^{15}$
考慮每一位二進位制的拆解
#include <bits/stdc++.h> #define mod (ll)10000007 using namespace std; typedef long long ll; const int maxn=50+5; ll n,a[maxn],ans=1,cnt; ll qpow(ll a,ll b) { ll res=1,base=a;二進位制位拆解while(b) { if(b&1) res=res*base%mod; base=base*base%mod; b>>=1; } return res; } int main() { scanf("%lld",&n); for(int j=49;~j;j--) { for(int i=49;~i;i--) a[i]+=a[i-1]; if(n>>j&1) a[cnt++]++; } a[cnt]++; for(inti=1;i<=49;i++) ans=ans*qpow(i,a[i])%mod; printf("%lld\n",ans); return 0; }
記憶化搜尋:
$10^{15}$分解二進位制位最多有五十位,可以列舉一共有多少位有1,50次
$dp[i][j][k]$表示在當前無限制條件下,已經列舉到了第$i$位,有了$j$個1,求有$k$個1的方案數。
結束條件$j=k$時才$return 1$。
#include <bits/stdc++.h> #define mod 10000007 using namespace std; typedefdfs版long long ll; int a[55]; ll n,dp[55][55][55],ans[55]; ll qpow(ll x,ll y) { ll res=1; while(y) { if(y&1) res=res*x%mod; x=x*x%mod; y>>=1; } return res; } ll dfs(int cnt,int flag,int now,int k) { if(now==k) return 1; if(!cnt) return now==k; if(!flag&&~dp[cnt][now][k]) return dp[cnt][now][k]; ll res=0; int Max=flag?a[cnt]:1; for(int i=0;i<=Max;i++) { res+=dfs(cnt-1,flag&&(i==Max),now+(i==1),k); } if(!flag) return dp[cnt][now][k]=res; return res; } ll query(ll x) { int cnt=0; ll res=1; while(x) { a[++cnt]=x&1; x>>=1; } for(int k=1;k<=50;k++) { for(int i=0;i<=a[cnt];i++) { ans[k]+=dfs(cnt-1,(i==a[cnt]),(i==1),k); } } for(int i=1;i<=50;i++) { res*=qpow(i,ans[i]); if(res>=mod) res%=mod; } return res; } int main() { memset(dp,-1,sizeof(dp)); scanf("%lld",&n); printf("%lld\n",query(n)); return 0; }