背景:离散化一词常常见到,甚至很多算法题不自觉的都会用到,比如hashmap,根据value找key等(内置find效率较低),根据acwing的例题总结了一下:
例题:https://www.acwing.com/problem/content/804/
所谓离散化,就是将一些定义域很大的数,但总数较小,就可以映射到一个新的数组。
假设我们从输入中得到案例的key-value,往往想搞成以下类似形式:
{0,1},{1,100},{2,1000},{3,2234},{4,99999999},{5,100}...
但是题设是这样的形式
{1,?},{100,?},{1000,?},{2234,?},{99999999,?},{100,?}...
ps.每对数中第一个为数组下标,第二个是增加值的大小
这样的k-v关系使得我们很难创建一个如此之大的数组空间。于是得从数据结构下手,创建vector<pair<int,int>>,批量地增加值再输出区间和 ,我们可以使用前缀和来有效降低轮询时间复杂度。
我们将k-v关系保存在一个存储pair的vector 记为add[]
vector value {{1,0},{100,2},{1000,3},{2234,3},{99999999,4},{100,5}...}
vector key 0 1 2 3 4 5
再单独创建数组记为alls[]={1,100,1000,2234,999999999,100}
我们注意到此时alls[]有两个相同的值,一般情况下我们可能会需要对alls进行去重。(sort+erase(unique,end))
方式一:sort+unique
vector <int> v;
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end);
unique(it1,it2);//先将所有重复元素排在尾部,返回第一个重复元素的指向迭代器
方式二:生成时count()==0判断是否有,无重复再push_back。
去重后:alls[]={1,100,1000,2234,99999999} 此时值就是唯一
此时如果遍历 add, it.first 即为值,本题中我们将it.second设为累加值
体现离散化思想的操作来了:
我们使用一个 int find(int value)函数来返回一个 (0,1,2,...n)区间的值。传入it.first(这里是找下标操作,可以用stl里面的find内置函数解决,但复杂度高)
构造a[N]存储累加值,大小由key范围决定
i 1 2 3 4 2.....
a[i] 100 40 22 12 40......
(it.first,it.second) {1,100} {100,20} {1000,22} {2234,12} {100,20}...... (先看这一排,再去找i,a[i])
s[i] 100 120 142 154....
ps. i=2时a[i]和it.second不等是因为有重复的it.first=100;
最后前缀和就可以用在轮询区间和上了。
讲了这么多,其实一个图就能讲明白.jpg:
|---------------------------------|----------------------------------|
索引: -777777 -1 0 1 999999
转换为:
|{-777777,?} {-1,?} {1,?} {9999999 ,?}|
索引: 0 1 2 3
加上
{-777777 ,-1 , 0 , 1 , 999999}
代码:
#include <iostream>
#include <vector>
#include <algorithm>
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(l);
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;
}