题目
定义一个数 \(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;
}