离散化
- 题目
你会这个题 吗?
题目
假定有一个无限长的数轴,数轴上每个坐标上的数都是 0 0 0。
现在,我们首先进行 n n n 次操作,每次操作将某一位置 x x x 上的数加 c c c。
接下来,进行 m m m 次询问,每个询问包含两个整数 l l l 和 r r r,你需要求出在区间 [ l , r ] [l, r] [l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 n n n 和 m m m。
接下来 n n n 行,每行包含两个整数 x x x 和 c c c。
再接下来 m m m 行,每行包含两个整数 l l l 和 r r r。
输出格式
共 m m m 行,每行输出一个询问中所求的区间内数字和。
数据范围
− 1 0 9 ≤ x ≤ 1 0 9 -10^9 \le x \le 10^9 −109≤x≤109,
1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1≤n,m≤105,
− 1 0 9 ≤ l ≤ r ≤ 1 0 9 -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
由于这个题目当中的位置(比如 x, l, r)的大小都是10的9次方的级别,开数组是开不了那么大的。
而这些需要用到的位置的个数加起来有 n + 2*m个的,n 和 m 都是10的5次方的级别的。
所以所有的数字都非常的 散 在 坐标轴上,密度非常低。
所以离散化就是将所有需要用到 的坐标挤在一起,中间没有一个空位。
准备阶段:
本题的思路是:
- 将所有用到的位置坐标存到alls容器,将n次操作的 x 和 c 存到adds容器当中,将m次询问的 l 和 r 存到query 容器当中
- 将alls 容器内的元素 排序、去重。
- 将alls容器内的位置映射到 数组a上
- 遍历 adds 容器内元素 进行 n 次操作
- 求数组 a 的前缀和数组 放到 数组 s 当中。
- 遍历 query 容器,进行 m 次查询
操作1:
操作2:
用到sort 和 unique 记得包含头文件
操作3:
对于映射操作我们需要写一个 find 函数,其功能是给一个 alls容器内一个位置,返回一个映射到数组 a 上的一个位置。
这里其实就是 把 alls 容器里的 位置转换成了他们自己的坐标 + 1.
+1的原因是 因为后面要求前缀和,所以 从1开始会方便很多。
操作4:
执行 n 次操作
操作5:
求前缀和数组
操作6:
执行 m 次询问 ,公式 s[ r ] - s[ l - 1 ]
完整代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;const int N = 3e5+10;//需要用到位置的数量int n, m;
int a[N], s[N];typedef pair<int, int> PII;//省代码
vector<int> alls;//存储所有位置
vector<PII> adds;//存储n次操作
vector<PII> query;//存储m次询问int find(int x)
{int i = 0, j = alls.size() - 1;while (i < j){int mid = (i + j + 1) >> 1;if (alls[mid] <= x) i = mid;else j = mid - 1; }return i+1;
}int main()
{//存储scanf("%d%d", &n, &m);while (n --){int x, c;scanf("%d%d", &x, &c);adds.push_back({x, c});alls.push_back(x);//alls 只需要位置}while (m --){int l, r;scanf("%d%d", &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());//去重 unique 可以将重复的数字放到容器的后面并返回第一个// 重复的数字的迭代器//------------------------//执行 n 次操作for (auto seg: adds){a[find(seg.first)] += seg.second;//一定注意要find}//求数组 a 的前缀和数组for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];//执行 m 次询问for (auto seg: query){int l = find(seg.first);int r = find(seg.second);printf("%d\n", s[r] - s[l-1]);}return 0;}
完