1. 程式人生 > 其它 >洛谷 P4317 花神的數論題

洛谷 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\)\(s_i\) 為二進位制下有 \(i\) 個 1 的數的個數。

思考一下二者是不是相等,想明白這個之後我們就可以愉快的切題啦

別忘了開 \(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;
}

完結撒花~