2022-11-07 Acwing每日一题
本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!
今天来复习离散化的操作.
区间和
假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。
现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含两个整数 x 和 c。
再接下来 m 行,每行包含两个整数 l 和 r。
输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
算法原理
离散化是建立了自然数到一段数列的映射(value->key
),如将x
,l
,r
都放进alls
这个数组里,实现了数轴离散化成区间,重新建立了新的区间范围,从而能在更小的区间范围里去进行其他操作。
第一步是去重。将重复出现的地址数去除掉,并且进行排序(为了后面好二分)。
第二步是处理插入,即在alls
中找到要进行操作的位置x
,返回其在alls
中的位置作为数组a
的键key
,并添加对应的值c
(value
)。
第三步就是获得离散化后的数列a
的前缀和s
。
最后一步就是根据query
获得查询区间的端点l
和r
,利用前缀和公式即可得到这一段区间所有经过操作的数字之和。
代码实现
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
vector<PII> add,query;
vector<int> alls; // 存储所有的地址,不仅存储了增加操作的地址还由输出区间的地址,将无穷长的数轴给他分割成有限区间
int n,m;
int a[1000010],s[1000010]; // a数组是数轴离散化后的数组,对离散化后的数组求前缀和即可得到这一区间的和
int find(int x){
int l = 0,r = alls.size()-1; // alls中包含了需要操作的所有区间,因此搜索范围就是alls内存储的地址
while(l < r){
int mid = (l+r) >> 1;
if(alls[mid] >= x){
r = mid;
}
else{
l = mid+1;
}
}
return l + 1; // 因为考虑前缀和的边界,因此直接+1就不需要考虑这个了
}
int main(){
cin >> n >> m;
for(int i=0; i < n ; ++i){
int x,c;
cin >> x >> c; // 输入在x处的值增加c
add.push_back({x,c}); // 将这一对pair装进add中
alls.push_back(x); // 将x装进alls中
}
for(int i=0; i<m ; i ++){
int l ,r;
cin >> l >> r; // 输入查询区间
query.push_back({l,r}); // 将区间pair放入query中
alls.push_back(l); // 再将其放入alls中
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; // a[x]连续的原因是因为alls中得下标是连续的,即使是插入也是在从0到alls.size()-1的区间内插入
}
// 区间求和
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;
}