第一行输入两个整数
,用空格隔开,表示马路的长度和地铁施工区域数,满足
,
。
接下来
行,每行输入两个整数
,用空格隔开,表示第
个施工区域的起始点和终止点的坐标,满足
。
输出一个整数,表示移除所有施工区域内(包括端点)树后,剩余的树的总棵树数。
500 3 150 300 100 200 470 471
298
马路上共有棵树。施工区域 [150,300] 中含有
棵,[100,200] 中含有
棵,[470,471] 中含有
棵。三个区域的重叠部分 [150,200] 有
棵被重复移除,所以实际移除的树数为
,因此剩余
棵。
10 2 2 5 4 8
4
马路上共有棵树。区域 [2,5] 移除
棵,区域 [4,8] 移除
棵。重叠部分 [4,5] 有
棵被重复移除,所以实际移除
棵,剩余
棵。
l,m = map(int,input().strip().split()) regin = [] for _ in range(m): a,b = map(int,input().strip().split()) regin.append((a,b)) totals = l +1 remove_trees = overlaps = 0 removed = [False] * (l+1) for (a,b) in regin: for i in range(a,b+1): if not removed[i]: remove_trees += 1 removed[i] = True else: overlaps += 1 remain = totals - remove_trees print(remain)
l, m = map(int, input().split()) nums = [1] * (l + 1) for _ in range(m): left, right = map(int, input().split()) nums[left:right+1] = [0] * (right - left + 1) print(sum(nums))
/*
* 容斥原理
* 方法原理:利用容斥原理计算多个区间的并集大小(奇加偶减规则),总树数减去并集大小得到剩余数量;
* 通过格雷码遍历区间子集,避免重复计算重叠区域的树的数量。
* 时间复杂度:O(M×2^M)
* 空间复杂度:O(M)
* 适用场景:L极大(如1e9)、M极小(≤15)的场景(无法开辟大数组存储标记)
*/
#include <stdio.h>
int main() {
int L, M;
if (scanf("%d %d", &L, &M) != 2 || L < 1 || L > 10000 || M < 1 || M > 100) {
return 1;
}
// 总树数(0~L共L+1棵)
int T = L + 1;
// ls/rs存储各砍伐区间的左右端点,p用于格雷码遍历子集
int ls[100], rs[100], p = 1;
// 遍历每个砍伐区间
for (int i = 0; i < M; i += 1) {
int l, r;
if (scanf("%d %d", &l, &r) != 2 || 0 > l || l > r || r > L) {
return 1;
}
// 先减去当前区间的树数(假设无重叠)
T -= r - l + 1;
// 保存当前区间端点
ls[i] = l;
rs[i] = r;
// 容斥原理核心:遍历前i个区间的所有非空子集,修正重叠区域的重复减法
for (int j = 1; j < p; j += 1) {
// lc/rc暂存当前子集与当前区间的交集端点
int lc = l, rc = r;
// 格雷码转换:j ^ (j>>1) 高效遍历所有子集
for (int g = j ^ (j >> 1), k = 0; g > 0 && k < i; k += 1, g /= 2) {
if (g % 2 == 1) {
// 计算交集:左端点取大,右端点取小
if (lc < ls[k]) {
lc = ls[k];
}
if (rc > rs[k]) {
rc = rs[k];
}
// 无交集则退出当前子集计算
if (rc < lc) {
break;
}
}
}
// 有交集时,按子集大小奇偶性调整(奇加偶减)
if (rc >= lc) {
if (j % 2 == 1) {
T += rc - lc + 1; // 奇数子集:补回重复减去的交集
} else {
T -= rc - lc + 1; // 偶数子集:再减去多补的部分
}
}
}
// p翻倍,为下一次遍历所有子集做准备
p *= 2;
}
printf("%d", T);
return 0;
} /*
* 数组标记
* 方法原理:用数组标记树的状态(0=未砍伐/-1=被砍伐),通过memset批量标记砍伐区间;
* 最后累加数组值统计剩余未砍伐树的数量(0不影响,-1则总数量减1)。
* 时间复杂度:O(L+M)
* 空间复杂度:O(L)
* 适用场景:L适中(≤1e4)、追求代码简洁性的场景,新手易理解
*/
#include <stdio.h>
#include <string.h>
int main() {
int L,M;
if (scanf("%d %d", &L, &M) != 2 || L < 1 || L > 10000 || M < 1 || M > 100) {
return 1;
}
// 总树数(0~L共L+1棵)
int T = L + 1;
// t数组:标记树的状态(0=未砍伐,-1=被砍伐)
int t[T];
// 初始化:所有树未被砍伐
for (int i = 0; i < T; i += 1) {
t[i] = 0;
}
// 遍历所有砍伐区间,批量标记被砍伐的树
for (int i = 0; i < M; i += 1) {
int l, r;
if (scanf("%d %d", &l, &r) != 2 || 0 > l || l > r || r > L) {
return 1;
}
// memset按字节赋值:0xff对应int的-1,4*(r-l+1)是区间总字节数(int占4字节)
// 批量将t[l]~t[r]设为-1(标记为被砍伐)
memset(t + l, 0xff, 4 * (r - l + 1));
}
// 统计剩余树数:未砍伐(+0),被砍伐(-1)
for (int i = 0; i <= L; i += 1) {
T += t[i];
}
printf("%d", T);
return 0;
} /*
* 跳跃标记
* 方法原理:通过数组标记每个位置的“下一个未被砍伐的位置”,初始化后遍历砍伐区间完成标记;
* 再跳跃式遍历标记数组,快速计算剩余未砍伐树的数量,避免重复遍历已标记区间。
* 时间复杂度:O(L+M)
* 空间复杂度:O(L)
* 适用场景:L适中(≤1e4)、M任意的常规场景,效率高且逻辑直观
*/
#include <stdio.h>
int main() {
int L,M;
if (scanf("%d %d", &L, &M) != 2 || L < 1 || L > 10000 || M < 1 || M > 100) {
return 1;
}
// 总树数(0~L共L+1棵)
int T = L + 1;
// R数组:标记每个位置的「下一个未被砍伐的位置」
int R[T + 1];
// 初始化:所有位置未被砍伐,下一个未砍伐位置为自身
for (int i = 0; i <= T; i += 1) {
R[i] = i;
}
// 遍历所有砍伐区间,完成标记
for (int i = 0; i < M; i += 1) {
int l, r;
if (scanf("%d %d", &l, &r) != 2 || 0 > l || l > r || r > L) {
return 1;
}
// 优化:避免重复标记已处理的区间
if (r + 1 > R[l]) {
// 标记[l, r]区间:所有位置的下一个未砍伐位置设为r+1
for (int i = l; i < r + 1; i += 1) {
R[i] = r + 1;
printf("R[%d]=%d\n", i, R[i]);
}
}
}
// 跳跃遍历:快速计算剩余未砍伐树的数量
int i = 0;
while (i <= L) {
// 找到第一个被砍伐的位置(R[i]≠i)
for (; i <= L; i += 1) {
if (i != R[i]) {
printf("i=%d R[i]=%d\n", i, R[i]);
break;
}
}
// 减去当前被砍伐区间的长度,然后跳到区间终点继续遍历
T -= R[i] - i;
printf("T=%d\n", T);
i = R[i];
}
printf("%d", T);
return 0;
} /*
* 区间合并
* 方法原理:先通过插入排序将所有砍伐区间按左端点升序排列,再遍历合并重叠/包含区间;
* 仅计算实际被砍伐的总长度(避免重复减重叠区域),总树数减去该长度得到剩余数量。
* 时间复杂度:O(M²+M)
* 空间复杂度:O(M)
* 适用场景:L极大(如1e9)、M适中(≤1000)的场景,无大数组内存压力
*/
#include <stdio.h>
int main() {
int L,M;
if (scanf("%d %d", &L, &M) != 2 || L < 1 || L > 10000 || M < 1 || M > 100) {
return 1;
}
// 总树数(0~L共L+1棵)
int T = L + 1;
// ls/rs存储区间的左右端点
int ls[M], rs[M];
// 读取区间并插入排序(按左端点升序),为合并区间做准备
for (int i = 0; i < M; i += 1) {
int l, r;
if (scanf("%d %d", &l, &r) != 2 || 0 > l || l > r || r > L) {
return 1;
}
// 插入排序核心:将当前区间插入到已排序的位置
int j = i - 1;
// 向前遍历已排序区间,左端点更小则后移区间
while (j >= 0 && l < ls[j]) {
ls[j + 1] = ls[j];
rs[j + 1] = rs[j];
j -= 1;
}
// 将当前区间插入到正确位置
ls[j + 1] = l;
rs[j + 1] = r;
}
// 合并区间:R为当前合并区间的右端点(初始为第一个区间的右端点)
int R = *rs;
// 先减去第一个区间的长度
T -= R - *ls + 1;
// 遍历剩余区间,合并重叠/包含区间
for (int i = 1; i < M; i += 1) {
// 情况1:区间重叠且右端点超出当前合并区间 → 只减超出部分
if (ls[i] <= R && rs[i] > R) {
T -= rs[i] - R;
R = rs[i]; // 更新合并区间右端点
}
// 情况2:区间无重叠 → 减去整个区间长度
else if (ls[i] > R) {
T -= rs[i] - ls[i] + 1;
R = rs[i]; // 更新合并区间右端点
}
// 情况3:区间被包含 → 无需处理(避免重复减)
}
printf("%d", T);
return 0;
} import sys L, M = map(int, sys.stdin.readline().strip().split()) T = [1] * (L + 1) l_r=[] for _ in range(M): i = list(map(int, sys.stdin.readline().strip().split())) l_r.append(i) for l, r in l_r: for i in range(l,r+1): T[i]=0 print(sum(T))方法2(差分数组法):
import sys L, M = map(int, sys.stdin.readline().strip().split()) diff = [0]*(L+2) for _ in range(M): l, r = map(int, sys.stdin.readline().strip().split()) diff[l]+=1 diff[r+1]-=1 tree_count, count = 0, 0 for i in range(L+1): tree_count += diff[i] if tree_count == 0: count += 1 print(count)方法3(合并数组):
import sys L, M = map(int, sys.stdin.readline().strip().split()) intervals = [tuple(map(int, sys.stdin.readline().strip().split())) for _ in range(M)] intervals.sort() merged=[] for l, r in intervals: if not merged&nbs***bsp;l > merged[-1][1] + 1: merged.append([l, r]) else: merged[-1][1] = max(merged[-1][1], r) removed = sum(r - l + 1 for l, r in merged) print(L + 1 - removed)
l, m = map(int, input().split(" "))
node_lst = []
flag = 0
cnt = 0
for i in range(0, m):
x, y = map(int, input().split(" "))
node_lst.append([x, 1])
node_lst.append([y, -1])
node_lst.sort()
start_node = node_lst[0][0]
for j in range(0, len(node_lst)):
flag = flag + node_lst[j][1]
if flag == 0:
end_node = node_lst[j][0]
yc_cnt = end_node - start_node + 1
cnt += yc_cnt
if j + 1 < len(node_lst):
start_node = node_lst[j + 1][0]
print(l - cnt + 1)
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class ComparePair {
public:
bool operator()(const pair<int, int>& a, const pair<int, int>& b) const {
return a.first < b.first;
}
};
int main() {
int L, M;
cin >> L >> M;
vector<pair<int, int>> intervals;
intervals.reserve(M);
for (int i = 0; i < M; ++i) {
int x, y;
cin >> x >> y;
intervals.emplace_back(x, y);
}
sort(intervals.begin(), intervals.end(), ComparePair());
vector<pair<int, int>> merged_intervals;
if (!intervals.empty()) {
auto it = intervals.begin();
int cur_start = it->first;
int cur_end = it->second;
for (++it; it != intervals.end(); ++it) {
if (it->first <= cur_end + 1) {
cur_end = max(cur_end, it->second);
} else {
merged_intervals.emplace_back(cur_start, cur_end);
cur_start = it->first;
cur_end = it->second;
}
}
merged_intervals.emplace_back(cur_start, cur_end);
}
int covered_count = 0;
for (const auto& seg :
merged_intervals) {
covered_count += seg.second - seg.first + 1;
}
cout << (L + 1 - covered_count) << endl;
return 0;
} 这是我的答案,虽然跟使一样但是我觉得很好理解也很直接
#include <stdio.h>
int main() {
int L,M;
scanf("%d %d",&L,&M);
int tree[20000]={0};
for(int i=0;i<=L;i++){
tree[i]=1;
}
int l[M];
int r[M];
for(int i=0;i<M;i++){
scanf("%d %d",&l[i],&r[i]);
}
for(int i=0;i<M;i++){
for(int j=l[i];j<=r[i];j++){
tree[j]=-1;
}
}
int cnt=0;
for(int i=0;i<=L;i++){
if(tree[i]==1){
cnt++;
}
// printf("%d.%d ",i,tree[i]);
}
printf("%d",cnt);
return 0;
}