信奥赛CSP-J复赛集训(模拟算法专题)(5):P1047 [NOIP 2005 普及组] 校门外的树
信奥赛CSP-J复赛集训(模拟算法专题)(5):P1047 [NOIP 2005 普及组] 校门外的树
题目描述
某校大门外长度为 l l l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 1 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 0 0 的位置,另一端在 l l l 的位置;数轴上的每个整数点,即 0 , 1 , 2 , … , l 0,1,2,\dots,l 0,1,2,…,l,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。
输入格式
第一行有两个整数,分别表示马路的长度 l l l 和区域的数目 m m m。
接下来 m m m 行,每行两个整数 u , v u, v u,v,表示一个区域的起始点和终止点的坐标。
输出格式
输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。
输入输出样例 #1
输入 #1
500 3
150 300
100 200
470 471
输出 #1
298
说明/提示
【数据范围】
- 对于 20 % 20\% 20% 的数据,保证区域之间没有重合的部分。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ l ≤ 1 0 4 1 \leq l \leq 10^4 1≤l≤104, 1 ≤ m ≤ 100 1 \leq m \leq 100 1≤m≤100, 0 ≤ u ≤ v ≤ l 0 \leq u \leq v \leq l 0≤u≤v≤l。
【题目来源】
NOIP 2005 普及组第二题
AC代码:
#include<bits/stdc++.h>
using namespace std;
int l, m, x, y; // l:道路总长度,m:施工区域数量,x/y:施工区域起止点
int a[10010] = {0}; // 记录树的状态数组(0 未移除,1 已移除),默认初始化为0
int cnt = 0; // 统计被移除的树的总数
int main() {
cin >> l >> m; // 输入道路长度和施工区域数量
for (int i = 1; i <= m; i++) { // 遍历每个施工区域
cin >> x >> y; // 输入当前区域的起止点
// 标记区间 [x, y] 内的树为移除状态
for (int j = x; j <= y; j++) {
a[j] = 1; // 1 表示该位置的树被移除
}
}
// 统计所有被移除的树(遍历 0 到 l 的位置)
for (int i = 0; i <= l; i++) {
if (a[i] == 1) cnt++; // 累计标记为 1 的树
}
// 输出剩余树的数量(总树数 l+1 减去被移除的树 cnt)
cout << l + 1 - cnt;
return 0;
}
代码功能分析:
-
问题背景
题目模拟道路上的树木移除问题。道路长度为l
,共有l+1
棵树(位置编号 0 到 l)。给定m
个施工区域,每个区域需要移除区间[x, y]
内的所有树。最终要求计算移除后剩余的树的数量。 -
核心思路
- 标记法:通过数组
a
记录每个位置的树是否被移除。数组下标对应树的位置,值0/1
表示未移除/已移除。 - 两阶段处理:
- 标记阶段:遍历所有施工区域,将对应区间内的树标记为移除(置 1)。
- 统计阶段:遍历道路上的所有树,统计被移除的数量
cnt
,最终剩余树数 = 总树数l+1
-cnt
。
- 标记法:通过数组
-
复杂度与优化
- 时间复杂度:O(mΔ + l),其中 Δ 是平均区间长度。当 Δ 较大时(如接近 l),复杂度接近 O(ml + l)。
- 优化思路:对于大规模数据,可用差分数组或区间合并优化标记过程,将时间复杂度降至 O(m + l)。
-
关键细节
- 数组
a
的初始化利用了全局变量的特性(默认全 0)。 - 区间标记允许重叠,重复标记不影响结果(值保持为 1)。
- 最终遍历范围是
0
到l
(包含两端点),对应l+1
棵树。
- 数组
文末彩蛋:
关注并查看老师的个人主页,学习完整csp信奥赛完整系列课程: https://edu.csdn.net/lecturer/7901