1. 程式人生 > 其它 >NC16649 [NOIP2005]校門外的樹

NC16649 [NOIP2005]校門外的樹

NC16649 [NOIP2005]校門外的樹

題目

題目描述

某校大門外長度為L的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1米。我們可以把馬路看成一個數軸,馬路的一端在數軸0的位置,另一端在L的位置;數軸上的每個整數點,即0,1,2,……,L,都種有一棵樹。

由於馬路上有一些區域要用來建地鐵。這些區域用它們在數軸上的起始點和終止點表示。已知任一區域的起始點和終止點的座標都是整數,區域之間可能有重合的部分。現在要把這些區域中的樹(包括區域端點處的兩棵樹)移走。你的任務是計算將這些樹都移走後,馬路上還有多少棵樹。

輸入描述

第一行有兩個整數:L(1 <= L <= 10000)和 M(1 <= M <= 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)\)

#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;
}

資料範圍二(加強): \(L \leq 10^5\)\(M \leq 10^5\)

思路:離散化。端點代替區間,合併區間後求長與總長相減

時間複雜度 \(O(MlogM)\)

空間複雜度 \(O(M)\)

#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是總端點數

}

資料範圍三(再加強): \(L \leq 10^9\)\(M \leq 10^5\)

思路:離散化+差分。已經無法用陣列模擬區間,考慮模擬端點的差分值,累加目標區間長度。

時間複雜度 \(O(MlogM)\)

空間複雜度 \(O(M)\)

#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;

}