题意:给你almost-K-First-P-Prime, 如果一个数x有k个质因子,且这k个质因子包含且仅包含前p个质数满足条件。 让你求Σφ(x);
思路:首先我们这p个因子一定要有,也就是剩下k-p个因子是什么了,剩下的因子每个都可以取前p个质数中的任意一个。想到这就有一种背包的感觉,然后在不知道状态转移方程的情况下,我们知道状态转移方程中的选还是不选怎么更新,就要用到欧拉函数更新了:
- φ(p)=p-1(p是质数)
- φ(p*a)=(p-1)*φ(a)(p是质数且p不能整除a)
- φ(p*a)=p*φ(a)(p是质数且p|a)
然后我就想到这。。。。开始搜索题解,对我来说太玄学了,看别人也没什么过渡,我是想不到。
dp[i][j]直接存的是答案,当一个质数没出现过,用到性质2,dp[i][j] = dp[i-1][j-1] * (prime[j] - 1) , 一个质数出现过可以用性质3,dp[i][j] += dp[i-1][j] * prime[j]。
#include <bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f3f3f3f3f #define met(a, b) memset(a, b, sizeof(a)) using namespace std; const int N = 4000; const ll mod = 1000000007; bool vis[N]; ll prime[N], dp[505][505]; void get_prime(){ int pos = 0; vis[1] = 1; for(ll i = 2; i <= 3600; i++){ if(!vis[i]) prime[++pos] = i; for(ll j = 1; j <= pos && prime[j]*i <= 3600; j++){ vis[i*prime[j]] = 1; if(i%prime[j] == 0) break; } } } void init(){ dp[0][0] = 1; for(int i = 1; i <= 500; i++){ for(int j = 1; j <= i; j++){ dp[i][j] += dp[i-1][j-1] * (prime[j] - 1) % mod; //这个质数第一次被选 if(i-1 >= j) dp[i][j] = (dp[i][j]+dp[i-1][j]*prime[j])%mod; //这个质数之前选过 } } } int main(){ get_prime(); init(); int T, cas = 0; cin >> T; while(T--){ int k, p; cin >> k >> p; printf("Case %d: %lld\n", ++cas, dp[k][p]); } return 0; }