[Codeforces546D]Soldier and Number Game[dp][实现][素数筛][分解整数][数学]

原题链接:[Codeforces546D]Soldier and Number Game[dp][实现][素数筛][分解整数][数学]

题意分析:本题从实质上来说,就是给你两个整数a, b。求出a, a - 1, a - 2........b + 1这些整数能被拆分成多少个素数相乘,把每个的拆分结果相加起来。

例如 a = 10, b = 2. 那么ans = 1(3) + 2(4) + 1(5) + 2(6) + 1(7) + 3(8) + 2(9) + 2(10) = 14

解题思路:由于题目给的测试点t范围很大,这样就要把每个整数能拆分的个数预先处理。我个人采用动归的思想,dp[i]代表i能被拆分成的最大素数个数。所以有:如果这个数是素数,则有dp[i] = 1.否则dp[i] = dp[i / j] + 1;(i % j == 0 && j为素数)因为实质上,dp[i]和dp[i / j] 就是多了个素数j。筛选素数的时候要使用素数筛法,否则超时。另外,dp[i]最终还要处理为前i个dp[i]的和,这样给出的答案的时间复杂度就是O(1)了。

个人感受:昨晚比赛的时候思路都是有的。就是筛选素数的时候用了暴力筛选,最后dp也没有优化成和,果断被hack了TAT怪我太蠢。不过想到用dp我也是好开桑的说,和学长的代码比了下,我这个预处理CPU占用率几乎为0~~~另外,早上交的时候是用cin输入,还是没有吃够『大数据就要用scanf』的亏啊。。。。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 5000005;
typedef long long ll;
ll dp[MAXN];
bool is[MAXN]; //第i个数是否为素数
int prime[MAXN], p;

void seive() { //素数筛法
    memset(is, true, sizeof is);
    for(int i = 2; i <= MAXN; ++i) {
        if(is[i]) {
            prime[p++] = i; //把素数存下来
            for(int j = i * i; j <= MAXN; j += i) is[j] = false;
        }
    }
}

int main() {
    seive();
    for (int i = 2; i < MAXN; ++i)
    {
        if (is[i]) //如果第i个是素数
        {
            dp[i] = 1;
            continue;
        }
        for (int j = 0; j < p; ++j) //找到第一个是它整除的数
        {
            if (i % prime[j] == 0)
            {
                dp[i] = dp[i / prime[j]] + 1;
                break;
            }
        }
    }
    for (int i = 3; i < MAXN; ++i) //处理为前i个和
        dp[i] += dp[i - 1];
    int t;
    scanf("%d",&t);
    while (t --)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%I64d\n", dp[a] - dp[b]);
    }
    return 0;
}

你可能感兴趣的:(codeforces)