由于下标范围(-1e9,1e9),数组或者哈希表都会爆,数据很大但是数量有限,将相隔巨大的点映射到有限长度的数组中,即离散化
a[N],s[N]存放数据,前缀和
alls存所有下标(包括原数组和询问的,根据询问来选择下标来存数)
add表示位置x数值+c,query表示询问
简而言之,问题是求(l,r)区间的和,那么只要将所有询问的下标存下,再找出原数据中x的值在下标数组(a)中的位置并加上c即可。
对于询问,前缀和预处理将每次查询的复杂度降为o(1)
总复杂度O((n+2∗m)log(n+2∗m)),输入*查找
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e6 + 5;
int a[N], s[N]; //存数,前缀和
vector<int> alls;
vector<PII> add, query;
int findd(int x) //二分求出x对应的离散化的值
{ //第一个>=x的位置
int l = 0, r = alls.size() - 1;
while (l < r)
{
int midd = l + r >> 1;
if (alls[midd] >= x)
r = midd;
else
l = midd + 1;
}
return r + 1; //映射到1,2,3..n(从1开始)
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int x, c;
cin >> x >> c; //位置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);
}
sort(alls.begin(), alls.end()); //排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); //去重
for (auto item : add) //二分查找位置并处理(+c)
{
int x = findd(item.first);
a[x] += item.second;
}
for (int i = 1; i <= alls.size(); i++)
s[i] = s[i - 1] + a[i]; //预处理前缀和
for (auto item : query)
{
int l = findd(item.first), r = findd(item.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
借用题解top3的图理解