目录
1、pair
可以将两个数据合并成为一组数据
typedef pair<int, int> PII;//pair可以将两个数据合成一组
vector<PII> adds, query;//加值和询问都是成对
2、algorithm头文件
max | 返回两个元素中值最大的元素 |
min | 返回两个元素中值最小的元素 |
abs() | 返回元素绝对值 |
swap | 交换两个对象的值 |
reverse | 反转排序指定范围中的元素 |
sort | 排序 |
3、unique函数
#include<algorithm> iterator unique(iterator it_1,iterator it_2); //两个参数都是迭代器
unique的作用是“去掉”容器中相邻元素的重复元素,这里去掉要加一个引号,为什么呢,是因为它实质上是一个伪去除,它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址。
int num[10]={1,1,2,2,2,3,4,5,5,5};
int ans=unique(num,num+10)-num;
这时,返回的ans是5,而num中前5项就是1,2,3,4,5,(左闭右开!!! 返回值为去重后容器中不重复序列的最后一个元素的下一个元素。)
以上是C++自带的unique函数,接下来手写一个:
vector<int>:: iterator unique(vector<int> &a)
{
int j = 0;
for (int i = 0; i < a.size(); i ++ )
if (!i || a[i] != a[i - 1])
a[j ++ ] = a[i];
//a[0]~a[j - 1]为a中所有不重复的数
return a.begin() + j; //非重复序列最后一个元素的下一个位置
}
4、vector<int>::iterator
定义向量迭代器
例如,vector<int>::iterator it 就是定义了一个名字叫it 的向量迭代器
for(it=v.begin();it!=v.end();it++)
cout<<*it<<endl;
5、begin and end
-
begin:返回指向容器第一个元素的迭代器
-
end:返回指向容器最后一个元素下一个位置的迭代器
begin和end返回的具体类型由对象是否是常量决定:
- 如果对象是常量,begin和end返回const_iterator
- 如果对象不是常量,返回iterator
vector<int> v1;
const vector<int> v2;
auto itr1 = v1.begin();//itr1的类型是vector<int>::iterator,返回指向容器第一个元素的迭代器
auto itr2 = v2.end();//itr2的类型是vector<int>::const_iterator,返回指向容器最后一个元素下一个位置的迭代器
6、erase函数
iterator erase(iterator first, iterator last); 左闭右开 [first,last) (sort函数也是左闭右开)
7、AcWing 802
假定有一个无限长的数轴,数轴上每个坐标上的数都是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
(1)思路
n表示n个要加上值的下标,以及n个对应的加值。m表示m对区间端点。
add:存放n对数据,即下标x和对应的加值c
alls:存放n个加值的下标x(储存该下标x的空间对应的还有一个下标),以及2m个区间端点(l和r)
在开数组a时,由于数组a最大只需要(n+2m)的容量(此时为最坏情况,即加值下标与区间端点完全无重合),所以const int N = 300010;
可知,alls就是这道题目将会用到的所有下标。由于需要加值的下标x可能与区间端点l和r有重合,所以需要对alls数组去重。
// 去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
接下来,构造find函数,传进来原下标x,返回存储x的空间的下标+1(这个+1是为了使得到的下标区间在[1,alls.size()],方便使用前缀和,因为前缀和的下标都是从1开始的)。
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;
}
遍历add数组,将数组元素的第一个值(即各加值的下标)依次传入find函数,得到其在alls数组中对应的下标+1(即为离散化后的下标),然后在a[N]数组中的对应位置加值c
// 处理插入
for (auto item : add)
{
int x = find(item.first);
a[x] += item.second;
}
既然要去求区间的值之和,那么肯定要先求该区间的前缀和。随后,再去将l和r传入find函数依次对应到alls数组的下标(l,r,x离散化后的数值会发生变化,但是相对位置不会变),所以问题就转化为求离散化后的数组的前缀和之差,易解。
(2)题解
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;//pair可以将两个数据合成一组
const int N = 300010;
int n, m;
int a[N], S[N];
vector<int> alls;
vector<PII> adds, query;//加值和询问都是成对
//得到大于等于x的第一个下标加1,使得起始下标为1,便于前缀和
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;
//当l = mid时,须int mid = l + r + 1 >> 1; 否则边界问题
else l = mid + 1;
}
return r + 1;//或为r + 1,此时l = r;
}
int main()
{
cin >> n >> m;
//输入adds
for (int i = 0; i < n; i ++ )
{
int x, c;
cin >> x >> c;
adds.push_back({x, c});//要用{},一组数据
alls.push_back(x);
}
//输入query
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);
}
//alls去重
sort(alls.begin(), alls.end());//头文件algorithm
alls.erase(unique(alls.begin(), alls.end()),alls.end());//unique函数头文件algorithm
//将原下标离散化为alls下标
for (auto item : adds)
{
int x = find(item.first);
a[x] += item.second;//因为初始化为0,所以+=也行 错 //需要注意只写=的话ac不了
//这里要考虑到item.first可能会出现重复,会覆盖之前的值
//adds数组无序
}
//求前缀和
for (int i = 1; i <= alls.size(); i ++ ) S[i] = S[i - 1] + a[i];
//求m个区间和
for (auto item : query)
{
int l = find(item.first), r = find(item.second);
cout << S[r] - S[l - 1] << endl;
}
return 0;
}