一、先搞懂什么是离散化,为什么要离散化?
为什么需要离散化:可能题目会给我们一堆数据,这堆数据值域很大可能是[0,1e9],但是这对数据个数很少,可能只有[0,1e5],那么就需要离散化!
什么是离散化:就是将每一个数据映射到 0 , 1 ,2 ,…… , n 数中。
现在我们举个例子:
那么就会随之而来两个问题:
1、a 数组中可能会有重复元素
针对这个问题,我们可以使用去重。
vector<int> alls; //存储所有待离散化的值
sort(alls.begin(),alls.end()); //排序
alls.erase(unique(alls.begin(),alls.end()),alls.end()); //将重复元素去重
对于第三步进行一个讲解:
2、如何计算离散化后的值(可使用二分!因为 a 是有序的)
int find(int x)//二分找到离散化后的值,找到第一个>=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 r + 1;//映射到 1 , 2 , …… , n
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 3e5+10;
int a[N],s[N];//a[]存储原序列 s[]用来求前缀和
int n,m;
vector<int> alls;//待离散化的数
vector<PII> add,query;//将操作数都存起来,要将操作的数进行离散化
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 r + 1;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
int x,c;
scanf("%d%d", &x, &c);
add.push_back({x,c});//将加的操作存起来
alls.push_back(x);//要操作的数待离散化
}
for (int i = 0; i < m; i ++ )
{
int l,r;
scanf("%d%d", &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)
{
int x = find(item.first);//将离散化后的对应的地方+c
a[x] += item.second;
}
for (int i = 1; i <= alls.size(); i ++ ) s[i]=s[i-1]+a[i];//前缀和 a[]下标从 1 开始
for(auto item : query)
{
int l = find(item.first) , r = find(item.second);//左右端点离散化
printf("%d\n",s[r]-s[l-1]);//前缀和求区间值
}
return 0;
}