NC16649 [NOIP2005]校门外的树

题目

题目描述

某校大门外长度为 \(L\) 的马路上有一排树,每两棵相邻的树之间的间隔都是 \(1\) 米。我们可以把马路看成一个数轴,马路的一端在数轴 \(0\) 的位置,另一端在 \(L\) 的位置;数轴上的每个整数点,即 \(0,1,2,\cdots,L\)都种有一棵树。

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

输入描述

第一行有两个整数:\(L\)\(1 \leq L \leq 10000\))和 \(M\)\(1 \leq M \leq 100\)),\(L\) 代表马路的长度,\(M\) 代表区域的数目,\(L\)\(M\) 之间用一个空格隔开。接下来的 \(M\) 行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

输出描述

包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。

示例1

输入

500 3
150 300
100 200
470 471

输出

298

题解

数据范围一(原题范围)

$L \leq 10^4 $ ,$ M \leq 10^2$

知识点:差分,枚举。

数组直接模拟整个区间,进行差分。

时间复杂度 \(O(L+M)\)

空间复杂度 \(O(L)\)

数据范围二(加强)

\(L \leq 10^5\)\(M \leq 10^5\)

知识点:离散化,枚举。

端点代替区间,合并区间后求长与总长相减。

时间复杂度 \(O(MlogM)\)

空间复杂度 \(O(M)\)

数据范围三(再加强)

\(L \leq 10^9\)\(M \leq 10^5\)

知识点:离散化,差分,枚举。

已经无法用数组模拟区间,考虑模拟端点的差分值,累加目标区间长度。

时间复杂度 \(O(MlogM)\)

空间复杂度 \(O(M)\)

代码

数据范围一(原题范围)

#include <bits/stdc++.h>

using namespace std;

int vis[10007];///模拟整个区间

int main(){
    int L,M;
    cin>>L>>M;
    for(int i = 0;i<M;i++){
        int l,r;
        cin>>l>>r;
        vis[l]++;
        vis[r+1]--;///差分
    }
    int a = 0,ans = 0;
    for(int i = 0;i<=L;i++){
        a += vis[i];///前缀和
        if(!a) ans++;///加完之后是0,那说明此处无遮盖
    }
    cout<<ans<<'\n';
    return 0;
}

数据范围二(加强)

#include <bits/stdc++.h>

using namespace std;

struct qj{
    int l,r;
}a[100007];

bool cmp(qj a,qj b){
    return a.l == b.l?a.r<b.r:a.l<b.l;
}

int main(){
    int L,M;
    cin>>L>>M;
    for(int i = 0;i<M;i++){
        cin>>a[i].l>>a[i].r;
    }
    sort(a,a+M,cmp);///按左端点从小到大,再按右端点从小到大

    int cnt = 0;///记录合并区间长度
    int l = a[0].l,r = a[0].r;///记录初始合并区间
    for(int i = 1;i<M;i++){
        if(a[i].l<=r) r = max(r,a[i].r);
        ///当前区间左端点小于等于此合并区间的右端点(不是上个区间的右端点,这里错了tmd)(因为排序,当前区间左端点不可能小于上一个区间左端点)
        ///那么合并区间的右端点为自己和当前区间右端点的最大值
        else{
            cnt+=r-l+1;
            l = a[i].l;
            r = a[i].r;
        }///合并结束更新
    }
    cnt+=r-l+1;
    cout<<L-cnt+1<<'\n';///L+1是总端点数

}

数据范围三(再加强)

#include <bits/stdc++.h>

using namespace std;

struct diff{
    int pos;
    int num;
}delta[100007];///记录特定端点代替整个区间,达到满足空间要求


bool cmp(diff a,diff b){
    return a.pos==b.pos?a.num<b.num:a.pos<b.pos;
}

int main(){
    std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int L,M;
    cin>>L>>M;
    for(int i = 0;i<M;i++){
        int x,y;
        cin>>x>>y;
        delta[i].pos = x;///记录左端点
        delta[i].num = 1;

        delta[i+M].pos = y+1;///记录右端点,因为差分所以y+1
        delta[i+M].num = -1;
    }
    sort(delta,delta+2*M,cmp);///根据左端点从小到大排序,先负再正
    int a = 0,cnt = 0;
    for(int i = 0;i<2*M;i++){
        a += delta[i].num;
        if(a == 1 && delta[i].num == 1){///此时pos是0区域的右端点+1位置,上一个pos即左端点位置
            cnt+=delta[i].pos - delta[i-1].pos;///相减即为区间长度
        }
    }
    if(!a) cnt+=L-delta[2*M-1].pos+1;
    cout<<cnt<<'\n';
    return 0;

}
posted @ 2022-05-08 20:27  空白菌  阅读(115)  评论(0编辑  收藏  举报