首页 > 试题广场 >

校门外的树

[编程题]校门外的树
  • 热度指数:15244 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}某校大门外长度为 L 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 的位置,另一端在 L 的位置;数轴上的每个整数点,即 0,1,2,\dots,L,都种有一棵树。

\hspace{15pt}由于马路上有一些区域要用来建地铁,这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。请你计算将这些树都移走后,马路上还有多少棵树。

输入描述:
\hspace{15pt}第一行输入两个整数 L,M,用空格隔开,表示马路的长度和地铁施工区域数,满足 1 \leqq L \leqq 100001 \leqq M \leqq 100

\hspace{15pt}接下来 M 行,每行输入两个整数 l_i,r_i,用空格隔开,表示第 i 个施工区域的起始点和终止点的坐标,满足 0 \leqq l_i \leqq r_i \leqq L


输出描述:
\hspace{15pt}输出一个整数,表示移除所有施工区域内(包括端点)树后,剩余的树的总棵树数。
示例1

输入

500 3
150 300
100 200
470 471

输出

298

说明

马路上共有 L+1=501 棵树。施工区域 [150,300] 中含有 151 棵,[100,200] 中含有 101 棵,[470,471] 中含有 2 棵。三个区域的重叠部分 [150,200] 有 51 棵被重复移除,所以实际移除的树数为 151+101+2-51=203,因此剩余 501-203=298 棵。
示例2

输入

10 2
2 5
4 8

输出

4

说明

马路上共有 11 棵树。区域 [2,5] 移除 4 棵,区域 [4,8] 移除 5 棵。重叠部分 [4,5] 有 2 棵被重复移除,所以实际移除 4+5-2=7 棵,剩余 11-7=4 棵。

备注:

用set
l, m = map(int, input().split())
s = set()
for i in range(m):
    a, b = map(int, input().split())
    s.update(range(a, b+1))
print(l + 1 - len(s))

发表于 2025-06-18 11:36:36 回复(0)
L, M = map(int, input().split())

trees = [1] * (L + 1)

for i in range(M):
    a, b = map(int, input().split())
    for j in range(a, b + 1):
        trees[j] = 0

print(sum(trees))


发表于 2025-07-07 15:38:40 回复(2)
L, M = map(int, input().split())
t = [1] * (L + 1)
for _ in range(M):
    l, r = map(int, input().split())
    for j in range(l, r + 1):
        t[j] = 0
print(sum(t))

发表于 2025-11-04 16:18:18 回复(0)
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)

发表于 2025-06-07 21:42:21 回复(1)
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))

发表于 2026-01-16 15:44:19 回复(0)
L, M = map(int, input().split())
# 初始化所有树都在
trees = [1] * (L + 1)  # 1表示有树,0表示被移走
for i in range(M):
    start, end = map(int, input().split())
    # 把这段区间的树都移走(标记为0)
    for j in range(start, end + 1):
        trees[j] = 0

shengyu_trees = sum(trees)
print(shengyu_trees)
正常施工路段应该不会重合吧
发表于 2025-09-26 11:22:07 回复(0)
n,M=map(int,input().split())
L=[1]*(n+1)
for _ in range(M):
    l,r=map(int,input().split())
    for i in range(l,r+1):
        L[i]=0
print(sum(L))
发表于 2025-06-24 16:16:05 回复(0)
#include <stdio.h>
#include <stdlib.h>
int main() {
    int L,M;
    scanf("%d %d",&L,&M);
    int *arr=(int*)malloc(L*sizeof(int));

    for(int i=0;i<=L;i++){
        arr[i]=1;//初始化树全在
    }
    for(int j=0;j<M;j++){
        int m,n;
        scanf("%d %d",&m,&n);
        for(int k=m;k<=n;k++){
            arr[k]=0;//区间内的树移除
        }
    }
    int len=0;
    for(int q=0;q<=L;q++){
        if(arr[q]==1){
            len++;
        }
    }//计算最终1的个数就是树的个数。这样不用考虑复杂的重合区间问题
    printf("%d\n",len);

   
    return 0;
}
发表于 2026-02-06 17:08:17 回复(0)
序列原来还能这样初始化
#include <bits/stdc++.h>
using namespace std;
int main() {
    int L,M,max,min,i,cnt=0;
    cin>>L>>M;
    vector <int> num(L+1,0);
    while(M--){
        int a,b;
        cin>>a>>b;
        for(i=a;i<=b;i++){
            num[i]=-1;
        }
    }
    for(i=0;i<=L;i++){
        if(num[i]>=0)
            cnt++;
    }
    cout<<cnt;
}
发表于 2026-01-25 19:45:41 回复(0)
import sys

if __name__=='__main__':
    data = []
    for line in sys.stdin:
        a = line.split()
        data.append(list(map(int,a)))

 
    road = [0 for _ in range(data[0][0]+1)]

    for i in range(1,data[0][1]+1):
        for j in range(data[i][0],data[i][1]+1):
            road[j] = 1
    print(road.count(0))

发表于 2026-01-16 19:36:06 回复(0)
/*
 * 容斥原理
 * 方法原理:利用容斥原理计算多个区间的并集大小(奇加偶减规则),总树数减去并集大小得到剩余数量;
 *          通过格雷码遍历区间子集,避免重复计算重叠区域的树的数量。
 * 时间复杂度: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;
}


发表于 2026-01-09 13:59:16 回复(0)
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int L = sc.nextInt();
        int M = sc.nextInt();
        int[] arr = new int[L+1];
        int count = 0;
        for(int i = 0; i < arr.length; i++){
            //1表示该位置有树
            arr[i] = 1;
        }
        for(int i = 0; i < M; i++){
          int l = sc.nextInt();
          int r = sc.nextInt();
          for(int j = l; j <= r; j++){
            //施工区间内位置标0,表示无树
            arr[j] = 0;
          }
        }
        for(int i = 0; i < arr.length; i++){
            if(arr[i] == 1){
                count++;
            }
        }
        System.out.println(count);
    }
}
发表于 2026-01-08 17:33:42 回复(0)
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
int main() {
    int l, n;
    cin >> l >> n;
    vector<PII> vec, result;
    while (n--) {
        int a, b;
        cin >> a >> b;
        vec.push_back({ a,b });
    }
    sort(vec.begin(), vec.end());
    int start = vec[0].first, end = vec[0].second;
    for (int i = 1; i < vec.size(); i++) {
        auto& ele = vec[i];
        if (end < ele.first) {
            result.push_back({ start, end });
            start = ele.first;
            end = ele.second;
        }
        else {
            end = max(end, ele.second);
        }
    }
    result.push_back({ start, end });
    int sum = 0;
    for (auto& k : result) {
        int diff = k.second - k.first + 1;
        sum += diff;
    }
    cout << l + 1 - sum;
    return 0;
}
发表于 2026-01-05 20:13:51 回复(0)
#include <stdio.h>
#include<stdlib.h>
int main() {
      int L,M,sum=0;
      scanf("%d %d",&L,&M);
      int a,b;
      int *p=(int*)calloc(L+1,sizeof(int));
      if(p==NULL)
          return 1;
      for(int i=0;i<=L;i++)
          *(p+i)=1;    
      for(int i=0;i<M;i++)
      {
         scanf("%d %d",&a,&b);
         for(int j=a;j<=b;j++)
         {
               if(*(p+j)==1)
               {
                    *(p+j)=0;
                    sum+=1;
               }      
         }
      }
      int s;
      s=L+1-sum;
      printf("%d",s);
      free(p);
      p=NULL;
      return 0;
}
发表于 2026-01-03 13:04:35 回复(0)
方法1(暴力):
把所有区间遍历,在区间内的树去除,否则保留。
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(合并数组):
将被移除的区间合并。
1.区间排序:将所有区间按照左端点进行排序,方便后续操作。
2.判断合并:
(1) 能合并的情况:完全重叠(包含关系),部分重叠(有交集),区间相邻(题目要求端点处的树也要删除)。
(2) 不能合并的情况:因为已经按照左端点排序,只需要判断已判断区间右端点和未判断区间左端点的大小。
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)



发表于 2025-12-23 12:06:22 回复(0)
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)
发表于 2025-12-19 16:09:23 回复(0)
#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;
}

发表于 2025-12-16 00:56:17 回复(0)
#include<bits/stdc++.h>
using namespace std;

int main()
{
    unordered_set<int>x;
    int l,m;
    cin>>l>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        for(int i=a;i<=b;i++) x.insert(i);
    }
    cout<<l-x.size()+1;
    return 0;
}
发表于 2025-12-09 23:14:35 回复(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;
}

发表于 2025-12-07 21:17:13 回复(0)
#include <stdio.h>
int main(){
    int l,m,min,max,arr[10001];
    int count = 0;
    scanf("%d %d\n",&l,&m);
    for(int i = 0;i<=l;i++)arr[i]=1;
    for(int i=0;i<m;i++){
        scanf("%d %d\n",&min,&max);
        for(int j = min; j <= max; j++){
            arr[j] = 0;
        }
    }
    for(int i = 0; i<=l;i++){
        if(arr[i] == 1)count ++;
    }
    printf("%d\n",count);
}
发表于 2025-11-15 16:51:54 回复(0)