一.概念
离散化数据是将一组不连续的数据转换为一组连续的整数,常用于处理具有大量重复值或需要连续编号的情况
例如,{400,5000,1,3000,200}通过离散化我们将其变为{3,5,1,4,2}.也就是说离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。(from百度)
基本步骤
1.排序
2.去重
3.离散化索引
二.例题
假定有一个无限长的数轴,数轴上每个坐标上的数都是0。现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。接下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
输入格式
第一行包含两个整数n和m。
接下来 n 行,每行包含两个整数x和c。
再接下里 m 行,每行包含两个整数l和r。
输出格式
共m行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
三.例题讲解
我们将l和r的大小范围看成数轴的范围,n和m看成点的坐标。
由于r和l的范围较大,而n和m的范围较小即为调用的数字较少,所以我们可以将其离散化,缩短数周的空间,将其离散化。
因此根据题目,我们可以得出此图
四.代码实现
#include<iostream>
#include<vector>
#include<algorithm>
/*
* 假定有一个无限长的数轴,数轴上每个坐标上的数都是0。
现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。
接下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
输入格式
第一行包含两个整数n和m。
接下来 n 行,每行包含两个整数x和c。
再接下里 m 行,每行包含两个整数l和r。
输出格式
共m行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
*/
using namespace std;
typedef pair<int, int> PII; //使用PII来代替pair<int,int>,从而使代码更加简洁易懂
const int N = 30010;
int n, m;//输入值
int a[N], s[N];//定义数组a[]和s[]
//alls为总的坐标
vector<int> alls;
vector<PII> add, query;
//函数功能:查找alls中x的下标
//函数返回值:alls中x所对应的下标+1
//二分搜索法
//用lowerbound也可以lower_bound(alls.begin(),alls.end(),x) - alls.begin() ;
int find(int x)
{
int l = 0, r = alls.size();
while (l < r)
{
int mid = (l + r) / 2;
if (alls[mid] >= x)
{
r = mid;
}
else { l = mid + 1; }
}
return r + 1; //为了计算前缀和方便所以+1
}
int main()
{
//接下来 n 行,每行包含两个整数x和c。
//再接下里 m 行,每行包含两个整数l和r。
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);//坐标放入adds数组中
}
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());
//去重
//unique返回一个新的结束迭代器,作为下一步的开始,alls.end()返回当前结束位置,删除这区间所有元素
alls.erase(unique(alls.begin(), alls.end()), alls.end());
//构造离散化数组a[i]
for (auto item : add)
{
int i = find(item.first);//构建[a]找到每个坐标x的位置
a[i] += item.second;//对a[]中每个元素加上c
}
//构造前缀和数组s[i]
//因为前面+1了所以这里i= 1
for (int i = 1;i <= alls.size(); i++)
{
s[i] = s[i - 1] + a[i];//获得前缀和
}
for (auto item : query)
{
int l = find(item.first), r = find(item.second);
//区间和就是s[r]-s[l-1],相当于一个线段
cout << s[r] - s[l - 1] << endl;
}
system("pause");
return 0;
}