【洛谷】4317:花神的数论题【数位DP】
题目背景
众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。
题目描述
话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。 花神的题目是这样的:设 sum(i)表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你 ∏i=1Nsum(i) ,也就是sum(1)∼sum(N)的乘积。
输入输出格式
输入格式:
一个正整数 N。
输出格式:
一个数,答案模 10000007 的值。
输入输出样例
说明
对于 100% 的数据,N≤10^15
Solution
数位DP
但是不是直接处理出乘积,而是枚举$i$,处理出有多少个数恰好有$i$个1.
最后直接用快速幂乘起来即可。
Code
#include<bits/stdc++.h> #define LL long long #define mod 10000007 using namespace std; LL n; LL mpow(LL a, LL b) { LL res = 1; for(; b; b >>= 1, a = a * a % mod) if(b & 1) res = res * a % mod; return res; } LL dp[55][2][55][55]; int num[55]; LL dfs(int dep, int up, int sum, int d) { if(!dep && sum == d) return dp[dep][up][sum][d] = 1; if(!dep) return dp[dep][up][sum][d] = 0; if(~dp[dep][up][sum][d]) return dp[dep][up][sum][d]; int tot = up ? num[dep] : 1; LL tmp = 0; for(int i = 0; i <= tot; i ++) tmp += dfs(dep - 1, up && i == tot, sum + i, d); return dp[dep][up][sum][d] = tmp; } LL ans[55]; LL cot(LL x) { int t = 0; memset(num, 0, sizeof(num)); while(x) { num[++t] = x % 2; x >>= 1; } for(int i = 1; i <= 50; i ++) { memset(dp, -1, sizeof(dp)); ans[i] = dfs(t, 1, 0, i); } LL res = 1; for(int i = 1; i <= 50; i ++) res = (res * mpow(i, ans[i])) % mod; return res; } int main() { scanf("%lld", &n); printf("%lld", cot(n)); return 0; }