acwing算法基础课笔记-整数离散化

前言

这里特指:整数的离散化,有序的离散化。

简概

有一些值较大的数,值域[0109][0\sim10^9],个数只有10510^5

存在问题:

有些题目要用到这些数字的值作为索引下标来使用,开辟一个大约 10910^9大小的数组不现实。(或者类似于数轴,无数多个值)

解决方案:进行映射,离散化

image-20211104160337746

使用场景:范围非常大,但是数值点分布特别稀疏。


进一步说明

例子

acwing 802. 区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。

现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。

数据范围:

109x109−10^9\le x \le 10^9

1n,m1051\le n,m \le 10^5

109lr109−10^9 \le l \le r \le 10^9

10000c10000−10000 \le c \le 10000

解题思路:

前缀和

所有用到的下标集中起来,进行排序,映射到从1开始的自然数

若x要加c,则将x映射到的a[k] += c (x映射到k)

本题中映射过来的下标k是代表着,数值x在原来alls数组中的次序(按从小到大),并且从1开始(便于计算前缀和)

image-20211110122725356

同理,若要求[l, r]区间上的和,则求a[kl,kr]a[k_l, k_r]之间的所有数和即可。(l映射到klk_l,r映射到krk_r)

image-20211110123342137

排序后,1映射到下标0,2映射到下标1,100映射到下标2,2000映射到下标3……

代码模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 300010; // 插入操作100000,查询操作100000+100000,总共30 0000 即n+2*m

int n, m;
int a[N], s[N]; // s[]是前缀和
// a[]保存的是映射过后的下标对应的值

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; // 注意一定要是≥x
else l = mid + 1;
}
return r + 1; // s[]下标从1开始
}

vector<int>::iterator unique(vector<int> &a)
{
int j = 0;
for (int i = 0; i < a.size(); i ++ )
if (!i || a[i] != a[i - 1])
a[j ++ ] = a[i];
// a[0] ~ a[j - 1] 所有a中不重复的数

return a.begin() + j;
}

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 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), alls.end());

// 处理插入
for (auto item : add)
{
int x = find(item.first);
a[x] += item.second;
}

// 预处理前缀和
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);
cout << s[r] - s[l - 1] << endl;
}

return 0;
}

关于unique函数

image-20211110130124091
1
2
3
4
5
6
7
8
9
10
11
vector<int>::iterator unique(vector<int> &a)
{
int j = 0;
for (int i = 0; i < a.size(); i ++ )
if (!i || a[i] != a[i - 1]) // 利用||运算符号的夹断效应
// 当i==0时直接执行下面语句,满足图中第一个条件
a[j ++ ] = a[i];
// a[0] ~ a[j - 1] 所有a中不重复的数

return a.begin() + j;
}