1、算法思路
这里我们分析的是一种整数、保序的离散化。所谓离散化,是针对这样一种情形。当我们要处理的数组元素的数量极大(有些题目干脆声明为无限大),而我们用到的元素个数很少(例如
1
0
5
10^5
105),我们就可以把这些用到的数映射成一个元素较少的数组。也就是将所有用到的数组序号转换成连续的自然数。所谓保序,就是这个元素较少的数组里面元素的先后顺序与原数组是相同的,也就是转换的时候,小的序号对应新数组小的序号,如下例的12312312312312312123123123对应0,12312312312312312123123124对应1一样。
例如:
假设我们只用到 12312312312312312123123123 12312312312312312123123124 两位
我们需要将数组映射为一个足够使用的数组
以后对那两个数的对应元素的操作就等价于在新数组中对0和1对应元素的操作。
明确之后,我们有一下两个问题需要处理:
- 一般题目中需要操作的元素未必是唯一的。也就是说可能会重复操作某些位置,所以应该将所有可能要操作的元素序号进行去重操作
- 如何快速找出一个原数组序号x对应的新数组下标
对于问题1,我们做如下处理:
vector<int>alls;//存储所有需要离散化的值,也就是所有我们可能要用到的下标
sort(alls.begin(),alls.size());//将所有值排序
alls.erase(unique(alls.begin(),alls.end()),alls.end());//去除重复元素
unique函数会去重,并把不重复的数组放到前面,把重复的数组放后面,返回不重复数组的最后一位,erase把他们删掉
对于问题2,我们使用二分法进行处理:
// 二分求出x对应的离散化的值
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;
}
2、例题
区间和
假定有一个无限长的数轴,数轴上每个坐标上的数都是 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
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
//因为有x、l、r三种操作数,所以数组要开三倍的10^5
const int N = 3e5 + 10;
typedef pair<int,int> PII;
//a用来存储元素的值,sum用来存储前缀和
int a[N],sum[N];
//因为我们需要对下标进行映射之后才有元素真正的坐标,所以需要将之前进行的操作先存起来,之后再重新进行操作。
vector<PII> add,query;
vector<int> alls;
//二分找大于等于x的第一个数
int findx(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 < 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.begin(),alls.end()),alls.end());
for(auto u : add)
{
a[findx(u.first)] += u.second;
}
for(int i = 1; i <= alls.size(); i ++)
{
sum[i] = sum[i - 1] + a[i];
}
for(auto u : query)
{
int l = findx(u.first);
int r = findx(u.second);
cout << sum[r] - sum[l - 1] << endl;
}
}