题目链接:http://lightoj.com/volume_showproblem.php?problem=1298
题意:给你两个数 n, p,表示一个数是由前 k 个素数组成的,共有 n 个素数,然后求这样的所有的数的欧拉和;
例如 n = 3, p=2; 前两个素数是2,3, 然后因为n=3,所以要再选一个素数组成一个数,有两种选择2*3*2=12 和 2*3*3=18 结果就是Φ(12)+Φ(18) = 10;
我们可以用dp[i][j] 表示前 j 个素数中选择 i 个的结果,Φ[n] = n*(p[i]-1)/p[i] * (p[i+1]-1)/p[i+1] * ... ;
那么对于p[j]是第一次选择的话,那么dp[i][j] += dp[i-1][j-1]*(p[j]-1),因为相当于是第i个素数是p[j],而且p[j]是第一次被选择,所以要*(p[j]-1)
当p[j]不是第一次被选择时,dp[i][j] += dp[i-1][j]*p[j],因为p[j]已经出现过一次了
要先打表再求,不然会TLE;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> #include <bitset> #include <iostream> #include <time.h> typedef long long LL; using namespace std; const int N = 5050; const double eps = 1e-10; const int INF = 0x3f3f3f3f; const int mod = 1000000007; int k = 1, p[1100]; bool f[N]; void Init() { for(int i=2; i<N; i++) { if(f[i])continue; p[k++] = i; for(int j=i+i; j<N; j+=i) f[j] = 1; } ///printf("%d ", k); } LL dp[505][505]; void Dp() { 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]*(p[j]-1); if(i>j)///只有当 i > j 时才能选p[j]大于二次; dp[i][j] += dp[i-1][j]*p[j]; dp[i][j] %= mod; } } } int main() { Init(); Dp(); int T, t = 1; scanf("%d", &T); while(T --) { int n, K; scanf("%d %d", &n, &K); printf("Case %d: %lld ", t++, dp[n][K]); } return 0; }