题意简述:
告诉你有n组数据,每组数据会给你两个值,分别是 L L L 和 R R R。 让你求在 L − R L-R L−R这个范围内所有数的因数个数之和。
题目分析:
这题其实就是一个技术!
-
首先给出一个 50 分 50分 50分的思路,就是暴力打表,反正也就 1 0 5 10^5 105,空间无论如何都是不会爆的。
-
再给出一个 100 分 100分 100分的思路。我们建立一个数组,分别记录从 1 − 2 × 1 0 6 1 -2 \times 10^6 1−2×106 所有数字的因数个数,每一次筛的方法就是从 1 1 1开始,一直枚举到 2 × 1 0 6 2 \times 10^6 2×106,然后求出每一个数的倍数,将这个倍数的个数 + 1 +1 +1。这样,整个复杂度就会降很多。但是这样还是不行。为什么呢?
-
仔细看题,你就会发现这样一句话:
对于 100 100 100%的数据 1 ≤ N ≤ 1 0 5 , 1 ≤ L , R ≤ 2 × 1 0 6 1≤N≤10^5,1≤L,R≤2\times10^6 1≤N≤105,1≤L,R≤2×106
-
我们无论如何,都是要两重循环的对吧?
所以复杂度一定会成为 O ( n m ) O(nm) O(nm)对吧?
那么我们怎么优化这个复杂度呢? -
我们可以做一个前缀和来优化这个 O ( n m ) O(nm) O(nm)的算法。这个前缀和 s u m [ i ] sum[\ i\ ] sum[ i ]为从 1 − i 1-i 1−i 所有的数的因数个数之和。这个操作只需要 2 × 1 0 6 2\times10^6 2×106次就可以完成,而 N N N次查询的复杂度就变成了 O ( 1 ) O(1) O(1)了!
代码:
(知道你们基本上直接看这边)
#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
using namespace std;
int in(){ //in()
int x=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
const int MAXN = 2e5;
int t,l,r;
int cnt[MAXN];
int sum[MAXN];
void init() { //初始化
for (int i = 1; i <= MAXN; i++)
for(int j = i; j <= MAXN; j += i)
cnt[j]++;
for (int i = 1; i <= MAXN; i++)
sum[i] = sum[i - 1] + cnt[i];
}
signed main(){
cin >> t;
init();
while (t--) {
cin >> l >> r;
cout << sum[r] - sum[l - 1] << endl;
}
return 0;
}
此代码做了放抄袭, 请大家一定要养成自己思考的习惯哦!