题解:AT_nikkei2019_final_d Deforestation

封面

洛谷专栏链接

题目:

在我写题解时题目翻译有错误,故此处放上简略的正确翻译。

NN 根竹子,编号从 11NN。初始时,所有竹子的高度是 00,时间每过一秒,每根竹子的高度增加 11

MM 个砍伐计划,第 ii 个计划在 TiT_i 时,将砍掉编号在 LiL_iRiR_i 之间的竹子。当竹子砍下时高度为 xx 时,会得到 xx 的竹子。被砍掉的竹子高度将变为 00,之后还会生长。

求可以得到多少竹子。

完整题目,点击此处

思路:

因为每过一秒高度都会增加一,且砍后会继续生长,所以我们可以只计算第 ii 个竹子最后是在什么时候被砍掉的,这个时间就是这个竹子的高度。

实现,在输入后我们可以直接从最后开始向前遍历,因为题目满足输入的时间 TiT_i 一定小于等于 Ti+1T_{i+1}

定义一个 set,类型为 int,名字叫 sese,用来标记这个竹子有没有砍过。

lower_bound 函数查找在 sese 里第一个大于等于 LiL_i 的值的位置,然后遍历编号在 LiL_iRiR_i 之间的竹子,加上现在的时间也就是可以从竹子身上得到的竹子,并把这个竹子从 sese 里删除。

代码:

#include<bits/stdc++.h>
using namespace std;
set<int>se;
int n,m;
int t[200005],l[200005],r[200005];
long long ans=0;
void solve(){
for(int i=1;i<=n;i++)se.insert(i);// 记录有没有砍
for(int i=m;i>=1;i--){
auto it=se.lower_bound(l[i]);// 二分查找第一个大于等于 l[i] 的值的位置
while(it!=se.end()&&*it<=r[i]){
ans+=t[i];
se.erase(it++);
}
}
cout<<ans<<"\n";
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>t[i]>>l[i]>>r[i];
}
solve();
return 0;
}