lk的梦想

众所周知,任何一个数我们都可以写成一些质数和 1 的乘积。 比如说 12 可以写作 也就是 ,由此我们可以很容易的求出他的约数的个数。 12 的约数个数就可以由他的质因子和 1 组合而来:。 算出来 L 到 R 区间所有数的平方的约数个数之和。 输入两个数 L, R(1 ≤ L ≤ R ≤ 107)。 输出一个数,表示约数的个数。

分析

如果一个数的平方的约数个数要求,我们可以考虑先求出这个数的约数个数,再求出这个数的平方,最后再求出这个数的平方的约数个数。

例如,如果我们想求出的平方的约数个数,首先我们求出的约数个数,可以得到个约数,即。求出的平方,即。最后,我们求出的约数个数,可以得到个约数, 即。因此,的平方的约数个数是

对于一个给定的数,我们可以通过以下步骤来求出它的平方的约数个数:

  1. 求出的约数个数
  2. 求出的约数个数
  3. 返回

下面我们来给出这个算法的一个实现。

首先,我们需要一个函数来求出的约数个数。我们可以使用如下的代码来实现这个函数:

int cnt(int n) {
    int res = 0;
    for (int i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            res += 2;
            if (i * i == n) --res;
        }
    }
    return res;
}

接下来,我们可以定义一个函数来求出区间所有数的平方的约数个数之和。我们可以使用如下的代码来实现这个函数:

int solve(int L, int R) {
    int res = 0;
    for (int i = L; i <= R; ++i) {
        res += cnt(i * i);
    }
    return res;
}

最后,我们可以在主函数中调用函数,并输出它的结果。

为了降低时间复杂度,我们可以将函数中的循环改为类似于函数中的方式来实现,即:

int solve(int L, int R) {
    int res = 0;
    for (int i = 1; i * i <= R; ++i) {
        int j = max(i * i, L);
        if (j <= R) res += (R - j) / i + 1;
    }
    return res;
}

这样,时间复杂度就降低到了

另外,我们还可以通过求出每个数的平方的约数个数,然后再统计区间和来实现这个算法。这样可以将时间复杂度进一步降低到

具体的,我们可以使用如下的代码来实现这个算法:

const int N = 1e7 + 5;
int cnt[N];
 
void init() {
    for (int i = 1; i < N; ++i) {
        for (int j = i; j < N; j += i) {
            cnt[j]++;
        }
    }
    for (int i = 1; i < N; ++i) {
        cnt[i] += cnt[i - 1];
    }
}
 
int solve(int L, int R) {
    return cnt[R] - cnt[L - 1];
}
 

在主函数中,我们需要先调用函数来求出每个数的平方的约数个数,然后再调用函数并输出结果即可。

完整代码

const int N = 1e7 + 5;
int cnt[N];
 
void init() {
    for (int i = 1; i < N; ++i) {
        for (int j = i; j < N; j += i) {
            cnt[j]++;
        }
    }
    for (int i = 1; i < N; ++i) {
        cnt[i] += cnt[i - 1];
    }
}
 
int solve(int L, int R) {
    return cnt[R] - cnt[L - 1];
}
 
int main() {
    init();
    int L, R;
    cin >> L >> R;
    cout << solve(L, R) << endl;
    return 0;
}