原题链接:[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(ll i = 2; i <= MAXN; ++i) {
if(is[i]) {
prime[p++] = i; //把素数存下来
for(ll 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;
}