## [$AcWing$ $422$. 校门外的树](https://www.acwing.com/problem/content/424/) ### 一、题目描述 某校大门外长度为 $L$ 的马路上有一排树,每两棵相邻的树之间的间隔都是 $1$ 米。 我们可以把马路看成一个数轴,马路的一端在数轴 $0$ 的位置,另一端在 $L$ 的位置;数轴上的每个整数点,即 $0$,$1$,$2$,……,$L$,都种有一棵树。 由于马路上有一些区域要用来建地铁。 这些区域用它们在数轴上的起始点和终止点表示。 已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。 现在要把这些区域中的树(包括区域端点处的两棵树)移走。 你的任务是计算将这些树都移走后,马路上还有多少棵树。 **输入格式** 输入文件的第一行有两个整数 $L$ 和 $M$,$L$ 代表马路的长度,$M$ 代表区域的数目,$L$ 和 $M$ 之间用一个空格隔开。 接下来的 $M$ 行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。 **输出格式** 输出文件包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。 **数据范围** $1$≤$L$≤$10000$,$1$≤$M$≤$100$ **输入样例**: ```cpp {.line-numbers} 500 3 150 300 100 200 470 471 ``` **输出样例**: ```cpp {.line-numbers} 298 ``` ### 二、桶排 模拟,数组遍历 $O(M*L)$ 定义一个长度为 $L+1$ 的布尔数组,表示每棵树的状态。 - $true$ 表示已经被移走; - $false$ 表示未被移走; 对于每次移动树木的操作 $[L_i,R_i]$,直接循环一遍,将布尔数组中从 $L_i$,到 $R_i$这段赋值为$true$。 最后统计值为 $false$ 的数量即可。 #### 时间复杂度分析 对于每次移动树木的操作,最坏情况下区间长度是 $O(L)$,因此计算量是 $O(L)$,一共有 $M$ 次操作,因此总时间复杂度是 $O(M*L)=100×10000=10^6$。 ```cpp {.line-numbers} #include using namespace std; const int N = 10010; int L, m; bool st[N]; int main() { cin >> L >> m; while (m--) { int l, r; cin >> l >> r; for (int i = l; i <= r; i++) st[i] = 1; } int res = 0; for (int i = 0; i <= L; i++) if (!st[i]) res++; printf("%d\n", res); return 0; } ``` ### 三、区间合并 先求出所有移动树木的操作的区间的并集,那么马路上剩余部分即为最终剩下树木的部分。 求所有区间的并集可以使用区间合并算法,可以参考 $AcWing$ $803$. 区间合并。 #### 时间复杂度 区间合并算法的时间复杂度是 $O(MlogM)$,其中 $M$ 是区间数量。 ```cpp {.line-numbers} #include using namespace std; typedef pair PII; const int N = 110; int m, L; PII seg[N]; int main() { cin >> L >> m; // 表示一个区域的起始点和终止点的坐标 for (int i = 0; i < m; i++) cin >> seg[i].first >> seg[i].second; // 按起点排序 sort(seg, seg + m); // L 代表马路的长度 int res = L + 1; // ① 第一个区间 int st = seg[0].first, ed = seg[0].second; // ② 从第二个区间枚举 for (int i = 1; i < m; i++) // ③ 枚举到的起点小于等于上一个终点,即有交集 if (seg[i].first <= ed) // ④ 交集分两种情况:(1)新的更长,(2)上一个更长 ed = max(seg[i].second, ed); else { // ⑤ 没有了交点,说明断档出现,开始新的区间 res -= ed - st + 1; // 所L+1中扣掉前一个整体区间的树的个数 // 全新的区间开始啦 st = seg[i].first, ed = seg[i].second; } // ⑥ 上面的逻辑,处理完成后,最后一个没有纳入到计算的范围内,所以需要再次扣除掉 res -= ed - st + 1; cout << res << endl; return 0; } ```