题目:
给你两个 正整数 l 和 r。对于任何数字 x,x 的所有正因数(除了 x 本身)被称为 x 的 真因数。
如果一个数字恰好仅有两个 真因数,则称该数字为 特殊数字。例如:
- 数字 4 是 特殊数字,因为它的真因数为 1 和 2。
 - 数字 6 不是 特殊数字,因为它的真因数为 1、2 和 3。
 
返回区间 [l, r] 内 不是 特殊数字 的数字数量。
示例 1:
输入: l = 5, r = 7
输出: 3
解释:
区间 [5, 7] 内不存在特殊数字。
示例 2:
输入: l = 4, r = 16
输出: 11
解释:
区间 [4, 16] 内的特殊数字为 4 和 9。
提示:
1 <= l <= r <= 109
没有技巧的暴力
思路:
直接暴力判断每个数是不是特殊数字即可
代码:
class Solution {
public:
    bool isSpecial(int num) {
        int cnt = 1;
        for (int i = 2; i < num; i++) {
            if (num % i == 0) cnt++;
        }
        return cnt != 2;
    }
    int nonSpecialCount(int l, int r) {
        int ans = 0;
        for (int i = l; i <= r; i++) {
            if (isSpecial(i)) ans++;
        }
        return ans;
    }
};
质数筛
思路:
特殊数字首先是一个平方数,并且除去自身和 1 之后的另一个因子一定是一个质数。这是因为:
	因子一般是成双成对的,若一个数字有奇数个因子,那么该数一定是平方数。
	该数除去自身和 1 仅有一个因子,因此该因子一定是质数。
因此,我们可以在 [1, r] 的范围内遍历所有质数,然后将它们的平方从 [l,r] 的范围中去除即可。
由于 r 的范围不超过 1e9 ,因此质数的遍历范围不超过 31622,而使用很简单的埃氏筛(复杂度为 O(nlognlogn),其中 n 为质数遍历范围)就可以轻松通过本题。
代码:
class Solution {
public:
    int nonSpecialCount(int l, int r) {
        int n = sqrt(r);
        vector<int> v(n + 1);
        int res = r - l + 1;
        for (int i = 2; i <= n; i++) {
            if (v[i] == 0) {
                if (i * i >= l && i * i <= r) {
                    res--;
                }
                for (int j = i * 2; j <= n; j += i) {
                    v[j] = 1;
                }
            }
        }
        return res;
    }
};