题目地址:
https://www.acwing.com/problem/content/description/804/
假定有个非常长的数轴,一开始每个坐标上的数都是 0 0 0。接着进行 n n n次操作,每次操作将某一位 x x x上的数加上 c c c。再接下来做 m m m次询问,每次询问区间 [ l , r ] [l,r] [l,r]所有数的和。
输入格式:
第一行包含两个整数
n
n
n和
m
m
m。接下来
n
n
n行,每行包含两个整数
x
x
x和
c
c
c。再接下里
m
m
m行,每行包含两个整数
l
l
l和
r
r
r。
输出格式:
共
m
m
m行,每行输出一个询问中所求的区间内数字和。
数据范围:
−
1
0
9
≤
x
≤
1
0
9
-10^9\le x\le 10^9
−109≤x≤109
1
≤
n
,
m
≤
1
0
5
1\le n,m\le 10^5
1≤n,m≤105
−
1
0
9
≤
l
≤
r
≤
1
0
9
-10^9\le l\le r\le 10^9
−109≤l≤r≤109
−
10000
≤
c
≤
10000
-10000\le c\le 10000
−10000≤c≤10000
求区间和可以用前缀和数组。但这里的主要问题是,如果我们开一个非常长的数组,然后统计每个位置被加了多少次,再算前缀和,这样非常浪费空间。我们可以采用离散化的方式来做。先将要用到的坐标存起来(显然每次操作的 x x x是要用到的,并且每次询问的 l l l和 r r r是要用到的。所以离散化后的数组要开长度为 n + 2 m n+2m n+2m即最大 300000 300000 300000),然后对其进行排序,排好序之后再去重(可能某个位置被加了多次,或者被询问多次)。接着进行操作,每次操作,就找到 x x x所在位置的下标(可以二分查找,因为要用到的下标都已经排好序了),然后在离散化后的数组 a a a中将那个位置增加 c c c;对于查询也是类似,查询开始之前先求一下 a a a的前缀和,然后先查一下 l l l和 r r r在离散化后的数组中的下标,接着根据前缀和数组来算区间和即可。代码如下:
#include <iostream>
#include <algorithm>
#include <vector>
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;
const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;
// 返回x离散化之后的值
int find(int x) {
int l = 0, r = alls.size() - 1;
while (l < r) {
int m = l + (r - l >> 1);
if (alls[m] >= x) r = m;
else l = m + 1;
}
return l + 1;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
int x, c;
cin >> x >> c;
add.push_back({x, c});
alls.push_back(x);
}
for (int i = 0; i < m; i++) {
int l, r;
cin >> l >> r;
query.push_back({l, r});
alls.push_back(l);
alls.push_back(r);
}
// 对alls做保序去重,以便做离散化
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
// 执行加数操作
for (auto item : add) {
int x = find(item.x);
a[x] += item.y;
}
// 求前缀和
for (int i = 1; i <= alls.size(); i++) s[i] = s[i - 1] + a[i];
for (auto item : query) {
int l = find(item.x), r = find(item.y);
cout << s[r] - s[l - 1] << endl;
}
}
时间复杂度 O ( ( n + 2 m ) log ( n + 2 m ) ) O((n+2m)\log (n+2m)) O((n+2m)log(n+2m)),空间 O ( n + 2 m ) O(n+2m) O(n+2m)。