A 数论の初见
解题思路:
首先使用Eratosthenes筛法求出内的素数,得到一个布尔数组vaild,vaild[i]表示数字i的素性,时间复杂度为O(MaxN*loglog(MaxN)),空间复杂度为O(MaxN)。
用数组sum存储所求答案,sum[i]表示小于等于i的素数的个数。由于sum[i]可以由sum[i-1]和vaild[i]得到,因此求出整个数组的时间复杂度为O(n),空间复杂度为O(n)。
对于每次询问n,只需要输出sum[n]即可,处理每次询问的复杂度都是O(1)。
综上,该解法的时间复杂度为O(MaxN*loglog(MaxN)+T),空间复杂度为O(MaxN),其中MaxN为N的最大值,T为询问组数。
Eratosthenes筛法:如果要判断在内的所有数的素性,在内从小到大枚举基数i,去掉范围内所有的i的倍数,枚举i直到超过为止,剩下的所有数都是素数。Eratosthenes筛法时间复杂度为O(n*loglogn),空间复杂度为O(n)。
附注:为什么在选定基数i后,不用考虑范围内的数?因为此范围内的数一定有比i小的因子,肯定在之前已经被删除出集合了。事实上,每个数在它的最小的素因子被选定为基数时,就一定会被删除出集合。
代码:
#include <iostream>
using namespace std;
const int MaxN = 1000000 + 7;
bool valid[MaxN];
int sum[MaxN];
int num;
//筛法求素数
void getPrime(int n) {
long long i, j;
for (i = 0; i <= n; ++i)
valid[i] = true; //筛选之前,都在集合里
for (i = 2; i <= n; ++i)
if (valid[i]) {
if (n / i < i) break;
for (j = i * i; j < n; j += i)
valid[j] = false; //去掉i的倍数
}
}
int main() {
getPrime(MaxN - 1);
sum[0] = sum[1] = 0; //边界情况,n=0或n=1
for (int i = 2; i < MaxN; i++)
if (valid[i]) //如果i是素数
sum[i] = sum[i - 1] + 1;
else //如果i不是素数
sum[i] = sum[i - 1];
while (cin >> num)
cout << sum[num] << "\n"; //O(1)处理每个询问
}
B 模式寻数
解题思路:
由于两个数组是相互独立的,因此题目要求即为分别求出两个数组中的最小值并相加。这里偷个懒,用std::nth_element()求出最小值,然后相加,程序相当简洁。
时间复杂度:O(n),空间复杂度:O(n)