一、离散化的含义
离散化就是把大而分散的一段段使用到的稀疏区间,整合映射到连续的一段较小的稠密区间里,然后就可以通过普通前缀和公式来计算连续一段的区间和,本质上就是化大为小,把稀疏离散化简为稠密连续的一段。
二、离散化面临的问题
1.可能有重复元素,去重。
vector<int> alls;
sort(alls.begin(),alls.end());
alls.erase(unique(all.begin(),alls.end()),alls.end());
//unique函数把重复元素排到尾部并返回重复元素段中第一个的指针。
2.如何算出离散化后的值。(二分法)
int findx(int x)//找到第一个大于等于x的位置
{
int left=0;
int right=alls.size()-1;
while(left<right)
{
int mid=left+right>>1;
if(alls[mid]>=x)right=mid;
else left=mid+1;
}
return right;
}
三、离散化运用实例
这一题是离散化和前缀和的结合。关键在于对函数findx映射关系的理解,下面代码中有详细注释。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=300010;
int n,m;
int a[N];//储存的是数
int s[N];//前缀和
vector<int> alls;//存的所有待离散化的值
vector<PII> add,query;//用一对数来存储两种操作
int findx(int x)//findx函数用于找到a中位置x在alls中能排到第几个
{
int left=0;int right=alls.size()-1;
while(left<right)
{int mid=(left+right)>>1;
if(alls[mid]>=x)right=mid;
else left=mid+1;
}
return right+1;//加1意思是映射到a中第几个(单纯为了方便后边前缀和,下标加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 left,right;
cin>>left>>right;
query.push_back({left,right});
alls.push_back(left);
alls.push_back(right);
//alls数组储存的是a中所有待离散化值的下标
}
//去重
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
//现在alls是一个有序的,无重复的储存下标的数组了
//这题有用的就是alls中的几个下标了,其他的都没有用,这几个值差较大,把它按照顺序排好映射到a上
//问题就转化为a数组的简单前缀和问题了,关键是映射公式findx的理解。
for(auto item:add)
{
int index=findx(item.first);
a[index]+=item.second;
}//离散化关键步骤,把稀疏的数离散化聚集在了a上;一一映射
for(int i=1;i<=alls.size();i++)
{
s[i]=s[i-1]+a[i];
}
for(auto item:query)
{
cout<<s[findx(item.second)]-s[findx(item.first)-1]<<endl;
}//记住findx找的是a中的下标
return 0;
}