离散化
此处特指整数的离散化:一个分布较散的数列映射到一个集中的数列上(序列中数的跨度比较大但是很稀疏),如:
a[] 1 3 100 2000 500000
↓ ↓ ↓ ↓ ↓
b[] 0 1 2 3 4
会出现以下问题:
- a[]中可能有重复元素(去重)
- 如何算出a[]离散化之后的值
解决方法:
vector<int> alls;//存储所有待离散化的值
sort(alls.begin(),alls.end())//将所有值排序
alls.erase(unique(alls.begin(),alls.end()),alls.end())//去掉重复元素
//二分求出x对应的离散化的值
int find(int x)
{
int i=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;
}
区间和
题目描述
假定有一个无限长的数轴,数轴上每个坐标上的数都是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
题解
数的范围是[−109, 109],所以如果用暴力就会直接爆掉。由于操作的次数是1e5
,所以其实数轴上的数最多只会有1e5
个需要操作,但是数的范围却分散在整个数轴范围的[−109, 109],所以考虑先对给出操作的数据进行离散化
处理,这样可将需要遍历的范围大大缩小。再在处理之后的数据上加上每次操作时加上的c
,所有处理完成之后再利用前缀和
完成询问。
那么现在要解决三个问题:
- 如何实现离散化
- 离散化过程中原数组中的元素有重合的情况该如何处理
- 如何定位原来的元素在离散化后的新数组中的位置
首先处理第一个问题:离散化处理的过程中我们常常使用vector
动态数组进行存储数据。我们将待处理的数据存储在add
动态数组中,每组数据以pair
的形式存储,first
存放出现的需要操作的数x
,second
要加上的数c
。待询问的数据以相同形式存储在query
动态数组中,first
存放区间左端点l
,second
存放区间右端点r
。用int
型动态数组存放所有出现过的待离散化的数。在输入时将所有的x
、l
、r
放到all
动态数组中用作接下来的离散化。最后用a
数组表示所有处理结束后的结果,用s
数组表示它的前缀和。
接下来处理第二个问题:all
数组中的重复的元素删除,利用unique
和erase
函数。
最后一个问题:我们使用二分查找
的方法找到待处理的数x
在all
数组中的位置,并将它的值赋予结果数组a
。(由于all
动态数组已经过了去重操作所以最后a
数组的下标和all
相同。)
最后经过前缀和操作完成。
代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 3e5 + 7;
int a[N], s[N];
vector<int> all;
vector<pair<int, int>> add, query;
int find(int x)
{
int l = 0, r = all.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (all[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main(void)
{
int n, m;
cin >> n >> m;
while (n--)
{
int x, c;
cin >> x >> c;
add.push_back({ x, c });
all.push_back(x);
}
while (m--)
{
int l, r;
cin >> l >> r;
query.push_back({ l, r });
all.push_back(l);
all.push_back(r);
}
//去重
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
//插入
for (auto i : add) a[find(i.first)] += i.second;
//预处理前缀和
for (int i = 1; i <= all.size(); ++i) s[i] = s[i - 1] + a[i];
//处理询问
for (auto i : query) cout << s[find(i.second)] - s[find(i.first) - 1] << endl;
return 0;
}
TIP
unique()
函数是一个去重函数,STL
中unique
的函数unique
的功能是去除相邻的重复元素(只保留一个)。还有一个容易忽视的特性是:它并不真正把重复的元素删除,而是把重复的元素移到后面去,依然保存到了原数组中,最后返回去重后最后一个元素的地址。
它是
c++
中的函数,所以头文件要加#include<iostream>
,#include<algorithm>
,具体用法如下:因为
unique
去除的是相邻的重复元素,所以一般使用之前都会要排一下序。注意:
unique
函数并非是真正删除了元素,所以一般要与erase
成员函数或resize
成员函数互相配合使用。