洛谷 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;
}

完结撒花~

posted @ 2021-08-05 20:12  xixike  阅读(33)  评论(0编辑  收藏  举报