声明
本文资料参考acwing算法基础课
地址:https://www.acwing.com
概述
- 解决问题:把下标范围很大但是多数无效的数组,映射到一个较短的数组
- 和一般的哈希表不同,这里下标必须保持有序(对应map)
- 查找时间复杂度为O(logn),(使用二分)
- 思路:把有用的下标存到一个数组中,内容存到另一个数组中,这两个数组一一对应,使用二分查找原下标对应的新下标
模板记忆
开设数组:
- alls:记录原下标
- a[N]:记录对应下标的内容
- s[N]:附加的使用前缀和求范围数大小
- add,query:存储各种操作的数组,由于确定alls前必须把所有有用的数都存下来,所以不同用途的数字分开保留
这个模板分为四个部分:
- 去重:把一切可能用到的数字放入alls,然后去重
- 插入内容:用原下标查找新下标并插入内容数组
- 查找:根据原下标求新下标(二分)
模板代码
// 1所有用用的数字放入alls后,用以下语句去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
// 2插入数组内容
for (int i = 0; i < n; i ++ )
{
int tag = find(add[i].first);
a[tag] += add[i].second;
}
// 3输入原下标,查找新下标
int find(int x)
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] == x) return mid + 1;
else if (alls[mid] < x) l = mid + 1;
else r = mid - 1;
}
return l + 1; // 这里返回的是+1是因为要用前缀和
}
离散化 — 区间和
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int a[N], s[N];
vector<PII> add, query;
vector<int> alls;
int find(int x) // 根据原下标查新下标,二分法
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] == x) return mid + 1;
else if (alls[mid] < x) l = mid + 1;
else r = mid - 1;
}
return l + 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}); // 把所有加存在add里
alls.push_back(x); // 对应下标存起来
}
for (int i = 0; i < m; i ++ )
{
int l, r;
cin >> l >> r;
query.push_back({l, r}); // 把所有查找存在query里
alls.push_back(l); // 对应下标存起来
alls.push_back(r);
}
// 去重排序
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
// 进行加操作
for (int i = 0; i < n; i ++ )
{
int tag = find(add[i].first);
a[tag] += add[i].second;
}
// 前缀和
for (int i = 1; i <= alls.size(); i ++ ) s[i] = a[i] + s[i - 1];
// 进行查找操作
for (int i = 0; i < m; i ++ )
{
int l = find(query[i].first);
int r = find(query[i].second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}