题目

定义一个数 \(x\) 的价值为:将这个数分解成若干个正整数 \(a_1, a_2, \cdots, a_k\)\(k\) 是任意可行正整数)的乘积形式\(x = a_1 \times a_2 \times \cdots \times a_k\)\(a_i\) 只能有一个质因子,且对于 \(\forall \ i \neq j\),都满足 \(\gcd(a_i, a_j) = 1\),即两个数互质。

分解之后,这些正整数的和 \(\sum a_i\) 就是这个数的价值。特殊定义:\(1\) 的价值是 \(0\)

多组询问,每次给定正整数 \(L, R\),求大小在 \(L \sim R\) 范围内的所有数的价值之和。

限制:

  • \(1 \leqslant T \leqslant 10^4\)
  • \(1 \leqslant L \leqslant R \leqslant 3 \times 10^7\)

算法分析

做法:欧拉筛

使用 ps 数组记录当前已经筛出的质数,使用 val[x] 记录 \(x\) 的价值

原理:欧拉筛中通过 if (i%ps[j] == 0) break; 控制每个合数只能被它的最小质因子标记,使得使得时间复杂度达到线性。

对于质数 \(i\)val[i] = i
对于合数 i*ps[j] 有两种情况:

  • \(i\) 的最小质因子大于 \(ps[j]\),那么 val[i*ps[j]] = val[i] + ps[j]
  • \(i\) 的最小质因子等于 \(ps[j]\),那么先分解出 \(i\) 里面 \(ps[j]\) 的最大次幂因子 \(x\),那么 val[i*ps[j]] = val[i] - x + ps[j]*x

然后通过前缀和预处理,每次询问做差分即可。

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

const int MX = 3e7+5;

int ps[MX/10], pf[MX];
ll val[MX];

void init() {
    int c = 0;
    for (int i = 2; i < MX; ++i) {
        if (!pf[i]) ps[++c] = i, pf[i] = i, val[i] = i;
        for (int j = 1; i*ps[j] < MX; ++j) {
            if (i%ps[j] == 0) {
                pf[i*ps[j]] = pf[i]*ps[j];
                val[i*ps[j]] = val[i] - pf[i] + pf[i*ps[j]];
                break;
            }
            else {
                pf[i*ps[j]] = ps[j];
                val[i*ps[j]] = val[i] + ps[j];
            }
        }
    }
    for (int i = 1; i < MX; ++i) val[i] += val[i-1];
}

int main() {
    freopen("stupid.in", "r", stdin);
    freopen("stupid.out", "w", stdout);
    
    init();
    
    int t;
    cin >> t;
    
    while (t--) {
        int l, r;
        cin >> l >> r;
        --l;
        cout << val[r]-val[l] << '\n';
    }
    
    return 0;
}