目录:算法日记
题目描述
在一个坐标轴上有条线段。
每条线段的每个端点的坐标都为整数。
可能存在退化成点的线段。
线段之间可以相互交叉、嵌套甚至重合。
请你计算,对于每个,坐标轴中共有多少个整数坐标的点满足恰好被 条线段覆盖。
注意,左右端点分别为 的线段覆盖点 当且仅当 。
输入格式
第一行包含整数 。
接下来 行,每行包含两个整数 ,表示一条线段的左右端点。
输出格式
一行 个整数,其中第 个整数表示坐标轴中满足恰好被 条线段覆盖的整数坐标的点的数量。
数据范围
前三个测试点满足 。
所有测试点满足 。
输入样例
3
0 3
1 3
3 8
输出样例
6 2 1
算法思路
根据题意,整数坐标被条线段覆盖可理解为每个整数坐标被线段覆盖的次数。
首先考虑朴素暴力做法:对每条线段覆盖的点进行遍历,遍历到每个整数点使其,最终统计每个整数点的值即可。
朴素做法时间复杂度为,其中为线段数量,为线段长度最大值,由题目的数据范围可知,该时间复杂度必会TLE。
注意到,因此考虑或做法。
处理每条线段相当于对线段范围内的整数点,因此利用差分可以快速实现给区间的每个数。
复习下差分的主要内容:
- 差分是前缀和的逆运算;
- 差分处理区间为左闭右开,出界要记得减去多加的数;
- 差分使用数组存储所有整数点;
这里由于,显然无法直接用数组进行处理,而线段条数,因此考虑对线段端点离散化。
使用map对线段端点进行映射,遇到端点再进行处理,其中,键为区间端点,值为被区间覆盖的次数。
需要注意的是,相邻离散化端点之间的整数点被覆盖的次数一致,可直接累加。
AC代码
#include<map>
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
LL res[N];
map<LL, int> dic;
int main() {
int n;
scanf("%ld", &n);
for(int i = 0; i < n; ++i) {
LL l, r;
scanf("%ld %ld", &l, &r);
dic[l] += 1;
dic[r+1] -= 1;
}
LL sum = 0; //被覆盖的总次数
LL last = -1;
for(auto& [k, v] : dic) { //map按键升序进行迭代
if(last != -1) res[sum] += k - last; //last到k之间的整数被覆盖次数一致
sum += v;
last = k;
}
for(int i = 1; i <= n; ++i) {
printf("%ld ", res[i]);
}
printf("\n");
return 0;
}