最强详解离散化算法(包一遍学会)

离散化算法

离散化使用的范围

首先给你一个序列,我们有一些数值,这些数值的范围比较大,值域可能是 [ 0 , 1 0 9 ] [0,10^9] [0,109],但是里面的个数是很少的,个数可能是 [ 0 , 1 0 5 ] [0,10^5] [0,105]​,有些时候我们需要利用这些值当作数组的下标来做,这是我们是不可能去开一个 1 0 9 10^9 109​的数组的,因此,我们就要将这个序列里面的数映射到一个从0开始的连续自然数。

但是在离散化的时候我们应该注意哪一些问题呢?

(1)序列里面的元素有可能是 重 复 \color{red}{重复} ​​​的,这就涉及到 去 重 \color{red}{去重} 的操作。

(2)如何快速算出 x x x​​(x表示序列里面的数值)离散化后新的数组的下标值。这里用 二 分 查 询 \color{red}{二分查询} 来实现。

做离散化的具体步骤

这里的离散化一定是保须的

1:首先将这个序列里面的数进行从小到大排序。

2:接下来就是去重操作,c++里并没有直接去重的函数,但是我们是可以用函数来实现的,接下来阐述一下去重的原理, u n i q u e unique unique函数是返回排序后里面没有重复元素的最后一个值的下标返回,其实后面有就是重复的数,所以我们通过 e r a s e erase erase函数将后面重复的函数删掉。

u n i q u e unique unique函数的手动实现

c++里面是有专门的 u n i q u e unique unique函数的,但是我们可不可以手动实现一个 u n i q u e unique unique函数ne?当然也是可以的,接下来我就讲讲这个函数的具体实现算法。

(1)将不重复的数全部选出来,满足下面两个条件中的一个条件就行

1:这个数是序列中的第一个数

2:这个数与他前面一个数是不相等的,即 a [ i ] ! = a [ i − 1 ] a[i]!=a[i-1] a[i]!=a[i1]

(2)实现我们通过双指针的方法

i i i指针表示遍历这个序列里面所有的数, j j j​指针表示当前存到的第几个不同的数

(3)注意,这里我们返回的是一个迭代器

vector<int>::iterator unique(vector<int>& a) {
	int j = 0;//j表示当前存下的第几个不同的数
	for (int i = 0; i < a.size(); i++)
		if (!i || a[i] != a[i - 1])
			a[j++] = a[i];
	return a.begin() + j;
}

3:如何快速算出 x x x离散化后的值呢?具体实现看代码

sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
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,因为前缀和处理的时候我们都是从下标1开始的
}

最强详解离散化算法(包一遍学会)_第1张图片

数轴上面有一些数,然后将他们离散化之后存在数组里面就是 a [ 1 ] = 5 , a [ 2 ] = 3 , a [ 3 ] = 6 , a [ 4 ] = 1 , … … , a [ 8 ] = 7 a[1]=5, a[2]=3, a[3]=6, a[4]=1, …… ,a[8]=7 a[1]=5,a[2]=3,a[3]=6,a[4]=1,a[8]=7

离 散 化 的 核 心 就 是 将 需 要 离 散 化 的 值 映 射 到 从 0 开 始 或 者 从 1 开 始 的 连 续 自 然 数 \color{red}{离散化的核心就是将需要离散化的值映射到从0开始或者从1开始的连续自然数} 01

练习题:

题意:

假定有一个无限长的数轴,初始时数轴的下标权威0,现在,首先进行n次操作,将位置为x的地方加上c,然后接下来有m次询问,每次询问包含两个整数l,r。我们要求的是 [ l , r ] [l,r] [l,r]区间里面的和

输入格式:

第一行两个数n和m;

接下来n行每行两个数x,c;

最后m次询问每行两个数l,r;

数据范围:

− 1 0 9 -10^9 109<= x x x<= − 1 0 9 -10^9 109

1<= n , m n,m n,m<= 1 0 5 10^5 105

− 1 0 9 -10^9 109​<= l l l<= r r r<= − 1 0 9 -10^9 109

− 10000 -10000 10000<= c c c<= 10000 10000 10000

#include
#include
#include
using namespace std;
/*
3 3
1 2
3 6
7 5
1 3
4 6
7 8
*/
const int N = 30010;
int a[N], s[N];//x离散化后的数组的下标所对应的c
typedef pair<int, int> PII;
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() {
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		int x, c;
		cin >> x >> c;
		add.push_back({ x,c });
		alls.push_back(x);
	}

	for (int i = 0; i < m; i++) {
		int l, r;
		cin >> 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 (int i = 0; i < add.size(); i++) {
		int x = find(add[i].first);
		//cout << x << endl;
		a[x] += add[i].second;
	}
	/*for (int i = 0; i < alls.size(); i++)cout << a[i] << endl;
	cout << endl;*/

	for (int i = 1; i <= alls.size(); i++)s[i] = s[i - 1] + a[i];
	
	for (int i = 0; i < query.size(); i++) {
		int l = find(query[i].first);
		int r = find(query[i].second);
		cout << s[r] - s[l-1] << endl;
	}
	return 0;
}

你可能感兴趣的:(模板题,算法,算法)