NC16649 [NOIP2005]校門外的樹
阿新 • • 發佈:2022-05-08
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;
}