LightOj 1298 - One Theorem, One Year(DP + 欧拉)

题目链接: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;

#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\n", 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\n", t++, dp[n][K]);
    }
    return 0;
}
View Code

 

posted @ 2016-11-04 17:14  西瓜不懂柠檬的酸  Views(363)  Comments(0)    收藏  举报
levels of contents