题目描述
首先明确一下题意,先输入两个整数n、m,n代表在区间[-1e9,1e9]某一点加一个整数的次数,输入x c在x处加上c,m代表求某个区间和的次数,输入l r求区间[l,r]的和。
分析
分析一下y总的代码。
主要分为5大步:
1.读输入。将每次读入的x c push_back()到add中,将每次读入的位置x push_back()到alls中,将每次读入的l r push_back()到query中。
2.排序、去重。
3.通过遍历add,完成在离散化的数组映射到的a数组中进行加上c的操作(用到find函数)。
4.初始化s数组。
5.通过遍历query,完成求区间[l,r]的和。
问题
1.为什么要在alls中需要alls.push_back(l);alls.push_back(r);?
首先要明确alls中存放的是位置而不是值,也就是存放的是x而不是c。
因为再求区间和的时候,我们提前分析到可以使用前缀和来做,求前缀和就需要下标l r,如果不加入l r到alls中的话,第5步中遍历时query就没有办法通过输入的l r去访问a或者s。因为find函数就是输入映射前的下标,返回在alls中的下标+1。
举个例子,拿平时的数组来说,下标都是整形,但是如果要求a[1.5]肯定是有错误的,在这里也一样。
2.为什么要排序和去重?
首先要明确find函数的功能,输入一个离散数组的位置(映射前的位置)x返回连续数组的位置+1(映射后的位置+1)。+1的目的是为了求区间和时少一步下标为0的判断。
排序很好理解,因为在find函数中是使用了二分来查找x在alls中的下标+1,想要使用二分alls就必须具有某种性质这里就可以找一个最简单的办法使他单调(但是y总说过二分!=单调性)。
#include <iostream> #include <vector> #include <algorithm> 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; 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; } 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; } 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), alls.end()); // 处理插入 for (auto item : add) { int x = find(item.first); a[x] += item.second; } // 预处理前缀和 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; }