原题链接:活动 - AcWing
问题简述:
假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。
现在,我们首先进行 nn 次操作,每次操作将某一位置 xx 上的数加 cc。
接下来,进行 mm 次询问,每个询问包含两个整数 ll 和 rr,你需要求出在区间 [l,r][l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 nn 行,每行包含两个整数 xx 和 cc。
再接下来 mm 行,每行包含两个整数 ll 和 rr。
输出格式
共 mm 行,每行输出一个询问中所求的区间内数字和。
数据范围
−1e9≤x≤1e9,
1≤n,m≤1e5,
−1e9≤l≤r≤1e9,
−10000≤c≤10000−10000≤c≤10000输入样例:
3 3 1 2 3 6 7 5 1 3 4 6 7 8
输出样例:
8 0 5
这是一道典型的离散化处理的题目 。
问题一:为什么不能用哈希或者是开个数组存呢?
很显然不行,因为这道题数据范围实在是太大了,所以但是并不是每个点都用上了,真正有过添加值的点最多不过1e5因此我们需要考虑的就是如何处理这1e5个数了。
首先,我们需要把所有的数映射到一个数组里面,这样就可以降低时间复杂度,那么如何实现呢?我们可以用一下思路。
原始数组:-1 -2 6 7 6
下标: 1 2 3 4 5
排列:-2 -1 6 6 7
下标: 2 1 3 5 4
重新排列下标并去重:-2 -1 6 7
下标: 1 2 3 4
ok经过上述推到我们手动模拟了离散化,现在我们只需要把给定区间的l和r通过二分找到前缀数组的左右边界就可以得到答案就是前缀和数组a[r]-a[l-1]。
代码实现:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int s[N],a[N];
int cnt=1,sz,max1=0,min1=1e9;
int findl(int x)//查询左边界
{
int ll=1,rr=sz;
while(ll<rr)
{
int mid=(ll+rr)>>1;
if(s[mid]>=x) rr=mid;
else ll=mid+1;
}
return ll;
}
int findr(int x)//查询右边界
{
int ll=1,rr=sz;
while(ll<rr)
{
int mid=(ll+rr+1)>>1;
if(s[mid]<=x) ll=mid;
else rr=mid-1;
}
return ll;
}
int main()
{
map<int,int> p;//用于储存每次相加后的值
int n,m;
cin>>n>>m;
while(n--)
{
int x,y;
cin>>x>>y;
p[x]+=y;
s[cnt++]=x;
max1=max(max1,x);//判断左右边界
min1=min(min1,x);
}
sort(s+1,s+cnt+1);
sz=unique(s+1,s+cnt+1)-(s+1);
for(int i=1;i<=sz;i++)
a[i]=a[i-1]+p[s[i]];
while(m--)
{
int l,r;
cin>>l>>r;
if(l>max1||r<min1) cout<<0<<endl;//如果该区间内没有加过任何数直接输出0
else
{
int cnt1=findl(l),cnt2=findr(r);
cout<<a[cnt2]-a[cnt1-1]<<endl;
}
}
return 0;
}