洛谷 P4317 花神的数论题
洛谷 P4317 花神的数论题
数位dp
这道题我个人认为还是有一定思维难度的
题目要求输出 \(ans = \prod\limits_{i=1}^nsum_i\),\(sum_i\) 为 \(i\) 在二进制下 \(1\) 的个数
那我们换个角度考虑, \(ans = \prod\limits_{i=1}^{len}{i^{s_i}}\),\(len\) 为最多有多少个 \(1\) ,枚举 \(i\) 就是枚举 1 的个数,\(s_i\) 为二进制下有 \(i\) 个 1 的数的个数。
思考一下二者是不是相等,想明白这个之后我们就可以愉快的切题啦
哦,对了,还有很重要的一点
\(dfs\) 过程中 \(ans+=dfs(...)\) 时不能取模
\(ans\) 计算的是指数,所以这跟 \(a^{p+q} \ne a^p+a^q\) 一个道理
最后,别忘了开 \(long\ long\)
附代码(有注释)
#include <iostream>
#include <cstdio>
#include <cstring>
#define mod 10000007
#define ll long long
using namespace std;
ll n, len;
ll num[60], sum[60]; //注意不是十进制,是二进制数,所以要开大一些
ll dp[60][60][2];
ll power(ll a, ll b){ //快速幂板子
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res % mod;
}
ll dfs(ll len, ll s, ll now, ll lim){ //s为1的个数,now为正在预处理的1的个数
if(!len) return s == now;
if(dp[len][s][lim] != -1) return dp[len][s][lim];
ll res = lim ? num[len] : 1; //注意是1,而不是9,啊啊啊我当时这里卡了好久
ll ans = 0;
for(ll i = 0; i <= res; i++)
ans += dfs(len - 1, s + i, now, lim && (i == res)); //不能取模,不能取模,不能取模(重要的事情说三遍)
return dp[len][s][lim] = ans;
}
ll solve(ll x){
len = 0;
while(x){
num[++len] = x % 2; //这里也是%2 /2
x /= 2;
}
for(ll i = 1; i <= len; i++){
memset(dp, -1, sizeof(dp));
sum[i] += dfs(len, 0, i, 1); //sum是上文的s
}
ll ans = 1;
for(ll i = 1; i <= len; i++)
ans = ans * power(i, sum[i]) % mod;
return ans;
}
signed main(){
scanf("%lld", &n);
printf("%lld\n", solve(n));
return 0;
}
完结撒花~