洛谷 P4317 花神的数论题

洛谷 P4317 花神的数论题

原题链接

数位dp

这道题我个人认为还是有一定思维难度的

题目要求输出 (ans = prodlimits_{i=1}^nsum_i)(sum_i)(i) 在二进制下 (1) 的个数

那我们换个角度考虑, (ans = prodlimits_{i=1}^{len}{i^{s_i}})(len) 为最多有多少个 (1) ,枚举 (i) 就是枚举 1 的个数,(s_i) 为二进制下有 (i) 个 1 的数的个数。

思考一下二者是不是相等,想明白这个之后我们就可以愉快的切题啦

哦,对了,还有很重要的一点

(dfs) 过程中 (ans+=dfs(...)) 时不能取模

(ans) 计算的是指数,所以这跟 (a^{p+q} e 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
", solve(n));
	return 0;
}

完结撒花~

本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15105209.html

原文地址:https://www.cnblogs.com/xixike/p/15105209.html