目录
1. 离散化思想介绍
2.算法模板
3.结语
我们有时候会遇到一些在特别大的区间进行操作,但是操作访问到的元素很少这样的情况。
这时便需要用到离散化处理。
1. 离散化思想介绍
用一个例题做讲解
一般我们会想采用创建数组,再使用前缀和来求解,这个做法在本题是不合理的。一是数组的长度很大,空间复杂度高,二是操作次数多,无效计算多,时间复杂度高。
那么我们该如何巧妙的解决这个问题呢?
这就用到了离散化处理,讲需要操作的下标作为值存到新的数组中,并通过新下标访问。
离散化:
创建新数组,在下标为 1 ,2 ,3 ,4 ,5的位置分别加上10 , 20 ,4 , 20 ,1即可。
访问的区间也同样需要离散化,如访问4~2000区间内的元素,离散化后,便为访问1~3区间内的元素,这样我们数据处理起来方便又快捷!
2.算法模板
思想都融在了代码里面,相信再多的文字描述不如跟着代码画图走一遍!!!!!
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int,int> PII;
const int N = 300000;
int n = 0, m = 0;
//a[N]存离散化后的操作结果,s[N]为a[N]的前缀和
int a[N],s[N];
//alls存放题目所需要操作的下标
vector<int> alls;
//add。query记录操作与访问的数据
vector<PII> add,query;
//二分查找离散化后alls内元素的下标
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;
}
}
//返回1,2,3……的下标(下标从1开始的数组)
return r + 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内存的是所有需要操作的下标
alls.push_back(x);
}
for(int i = 0;i < m; i++)
{
int l,r;
cin>>l>>r;
//记录下需要访问的区间
query.push_back({l,r});
//alls内存的是所有需要操作的下标
alls.push_back(l);
alls.push_back(r);
}
//对所有需要操作的下标排序,并且消除相同元素(因为下标唯一)
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
//遍历add中的元素,逐一进行操作
for( auto item : add)
{
int x = find(item.first);
a[x] += item.second;
}
//存放a[N]的前缀和
for(int i = 1; i <= alls.size(); i++)
{
s[i] = s[i-1] + a[i];
}
//离散化需要访问的区间,并求出结果
for( auto item : query)
{
int l = find(item.first);
int r = find(item.second);
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
3.结语
本文主要对离散化的算法进行一个总结与介绍,如果想要真正理解,还需要大家进行画图模拟代码的进行,以便形象理解与运用!!!