离散化
- 将一个值域很大,但是分布很稀疏的数列压缩映射到一个更小的数组当中,后面处理时可以大大减小复杂度
- 保序的离散化
- 需要考虑两个问题:
- 原数组中可能有重复元素
- 如何快速算出x离散化后的值,二分
区间和
//将一个值域很大,但是分布很稀疏的数列压缩映射到一个更小的数组当中,后面处理时可以大大减小复杂度
#include <iostream>
#include <algorithm>
#include <vector>
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;
//将x映射到新的稠密数组的一个位置;
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);//获取x映射到新数组后的下标
a[x] += item.second;//给位置x上的数加c
}
//预处理前缀和
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;
}
区间合并
- 常见应用题型为贪心
- 处理步骤:
- 按照所有区间的左端点排序
- 扫描所有区间,进行合并,合并时有三种可能的情况,分情况讨论处理
区间合并
//跟区间有关的很多问题思路都是贪心
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010;
int n;
vector<PII> segs;
void merge(vector<PII> &segs){
vector<PII> res;
sort(segs.begin(), segs.end());//pair优先用左端点排序
int st = -2e9, ed = -2e9;
for(auto seg : segs)
if(ed < seg.first) {
if(st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else ed = max(ed, seg.second);
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
int main() {
cin >> n;
for(int i = 0; i < n; i ++ ){
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}
merge(segs);
cout << segs.size() << endl;
return 0;
}