
https://www.acwing.com/problem/content/804/
本题的数据范围实在太大,用哈希表做不出来,但是你会发现数轴上的数字是离散的,就是数轴上的数不是一个挨着一个的,数轴上的数并不是特别多。那么我们就可以将数轴上的点映射到一个挨着的数组里。
本解析一步步的求出结果,且根据案例。 故边看案例边看代码使用效果最好。
总共需要的数组
- add : 保存真实的下标和相应的值
- alls : 用来保存真实的下标和想象的下标的映射关系
- query : 用来保存我们查询的左右两个端点
- a : 保存想象的坐标所对应的值
- s: 保存想象的坐标所对应的值的前缀和
第一步: 输入数轴上所对应的值
for(int i=0;i<n;i++)
{
int index,x; cin>>index>>x;
add.push_back({
index,x});//index 是我们真实的下标 x是数值
alls.push_back(index);// 将真实的下标和我们想象的坐标建立映射关系
}

第二步: 输入我们的查询的区间
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);
}
第三步: 将虚拟的坐标排序并去重
为啥去重:
是因为当我们输入
3 5
3 6
即给数轴上3的点加5 再加 6时。此时我们的坐标映射表里有了两个3 3 但其实它们对应的是同一个坐标。
故需要去重,排序。
sort(alls.begin(), alls.end());//排序
alls.erase(unique(alls), alls.end());//去重(坐标)
那么此时,我们来看一下,案例所建立的映射表是怎样的。

第四步:根据真的坐标,来找到对应虚拟的坐标,将其位置加上其相对应的数值。
根据真的坐标找其对应的映射的坐标,用二分来查找。
int find(int x)
{
int l=0,r=alls.size()-1;
while(l<r)
{
int mid=l+r>>1;
if(alls[mid]>=x) r=mid;
else l=mid+1;
}
return l+1;// 因为要求前缀和,故下标从1开始方便,不用额外的再处理边界。
}
for(int i=0;i<add.size();i++)
{
int x=find(add[i].first);
a[x]+=add[i].second;
}

此时方框中的数,就是该位置所对应的值。
注意那些值为0的坐标是区间的边界,因为没有值的大小故为0。引入它们的目的是为了方便求前缀和。
最后一步: 求前缀和,根据我们的查询的区间来输出区间的和
for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];
for(int i=0;i<query.size();i++)
{
int l=find(query[i].first),r=find(query[i].second);
cout<<s[r]-s[l-1]<<endl;
}
总的代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
typedef pair<int,

最低0.47元/天 解锁文章
967





