离散化的特点:
- 数据的值域比较大,个数比较少【一般l和 r较小的话,比如<=10^5,用前缀和】
- 把值域里的数映射到连续的自然数序列里
- 整个的值域跨度很大,但是值比价稀疏
- 这个映射其实就是把数组的值映射到其对应的下标
可能存在的问题 (我们用一个数组来存储值域里的那些数):
- a[ ]中可能有重复的元素,需要去重
- 如何做到快速映射(也就是如何快速算出x离散化后的值,即x对应的下标)【这部分的算法:二分】
模板的主要内容:
vector<int> alls; //***** 存储所有待离散化的值
sort( alls.begin(),alls.end() ); //将所有的值排序
alls.erase( unique( alls.begin(),alls.end(),alls.end() ) ); //去掉重复元素
【unique函数的原理:使得只出现一次的数在一个区域,
剩余的有重复的接在第一个end(最后未重复出现的位置的下标)的后面,
再把其与第二个end之间(数组末尾)的值erase】
// 二分求出对应的离散化的值
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~n,不 +1的话就是 0~n
}
【应用】例题:区间和
题目
假定有一个无限长的数轴,数轴上每个坐标上的数都是0。
现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。
接下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
输入格式
第一行包含两个整数n和m。
接下来 n 行,每行包含两个整数x和c。
再接下里 m 行,每行包含两个整数l和r。
输出格式
共m行,每行输出一个询问中所求的区间内数字和。
数据范围
−10^9 ≤ x ≤10^9,
1 ≤ n,m ≤ 10^5,
−10^9 ≤ l ≤ r ≤ 10^9,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
离散化完成后的状态
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 300010;
int n,m;
int a[N],s[N];
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()
{
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(1);
alls.push_back(r);
}
//去重
sort(alls.begin(),alls.end());
alls.erase( unique( alls.begin(),alls.end() ), 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;
}