离散化与区间合并
离散化
假定有一个无限长的数轴,数轴上每个坐标上的数都是 000。
现在,我们首先进行 nnn 次操作,每次操作将某一位置 xxx 上的数加 ccc。
接下来,进行 mmm 次询问,每个询问包含两个整数 lll 和 rrr,你需要求出在区间 [l,r][l, r][l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 nnn 和 mmm。
接下来 nnn 行,每行包含两个整数 xxx 和 ccc。
再接下来 mmm 行,每行包含两个整数 lll 和 rrr。
输出格式
共 mmm 行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109-10^9 \le x \le 10^9−109≤x≤109,
1≤n,m≤1051 \le n,m \le 10^51≤n,m≤105,
−109≤l≤r≤109-10^9 \le l \le r \le 10^9−109≤l≤r≤109,
−10000≤c≤10000-10000 \le c \le 10000−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 = 3e5 + 10;
vector<int> alls;
vector<PII> add, query;
int n, m;
int a[N], s[N];
// 查找对应下标
int find(int x){
int l = -1, r = alls.size();
while(l + 1 < r){
int mid = l + r >> 1;
if(alls[mid] >= x) r = mid;
else l = mid;
}
return r + 1;
}
int main(){
// 输入n,输入m
cin >> n >> m;
// 输入n个加数
while(n --){
int x, c;
cin >> x >> c;
alls.push_back(x);
add.push_back({x, c});
}
// 输入m个查询数
while(m --){
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);
int c = item.second;
a[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);
int r = find(item.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
分析
全局变量的定义
typedef pair<int, int> PII;
const int N = 3e5 + 10;
vector<int> alls; // 储存所有的要用的数
vector<PII> add, query; // 储存要加的位置和数 // 储存要查询的位置
int n, m;
int a[N], s[N]; // 用前缀和处理查询的数的范围和
处理n个加数
// 输入n个加数
while(n --){
int x, c;
cin >> x >> c;
alls.push_back(x); // 将要加的位置储存道alls里
add.push_back({x, c}); // 储存到add里
}
处理m个查询的位置
// 输入m个查询数
while(m --){
int l, r;
cin >> l >> r;
query.push_back({l, r}); // 将要查询的区间存储到query里面
alls.push_back(l); // 将要查询的位置储存到alls里面
alls.push_back(r);
}
去重
// 去重
sort(alls.begin(), alls.end()); // 对所有要用的位置排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去重
二分映射(二分查找下标)
// 查找对应下标
int find(int x){
int l = -1, r = alls.size();
while(l + 1 < r){
int mid = l + r >> 1;
if(alls[mid] >= x) r = mid;
else l = mid;
}
return r + 1;
}

加数
// 加数
for(auto item : add){
int x = find(item.first);
int c = item.second;
a[x] += c;
}
查询
// 查询
for(auto item : query){
int l = find(item.first);
int r = find(item.second);
cout << s[r] - s[l - 1] << endl;
}
其实离散化的核心关键点就在这个二分映射
就是我们将知道一个数,我们怎么知道它的脚标呢?
有的人就会想着用循环去遍历知道出现该数我们就停止就行
但是如果对于一个单调递增的数组,我们可以用二分法得到它的脚标
区间合并
给定 nnn 个区间 [li,ri][l_i, r_i][li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3][1,3][1,3] 和 [2,6][2,6][2,6] 可以合并为一个区间 [1,6][1,6][1,6]。
输入格式
第一行包含整数 nnn。
接下来 nnn 行,每行包含两个整数 lll 和 rrr。
输出格式
共一行,包含一个整数,表示合并区间完成后的区间个数。
数据范围
1≤n≤1000001 \le n \le 1000001≤n≤100000,
−109≤li≤ri≤109-10^9 \le l_i \le r_i \le 10^9−109≤li≤ri≤109
输入样例:
5
1 2
2 4
5 6
7 8
7 9
输出样例:
3
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
vector<PII> segs; // 定义区间
int n;
void merge(vector<PII> &segs){
vector<PII> res;
// 初始化
int st = -2e9, ed = -2e9;
// 遍历每个区间
for(auto seg : segs){
// 如果两个区间没有交集
if(ed < seg.first){
// 如果该区间不是第一个区间,答案存储+1
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;
// 输入n个区间
while(n --){
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}
// 对区间排序
sort(segs.begin(), segs.end());
// 合并区间
merge(segs);
//输出区间个数
cout << segs.size() << endl;
return 0;
}
358

被折叠的 条评论
为什么被折叠?



