题解:CF1249D2 Too Many Segments (hard version)

· · 题解

题目传送门

前言:

这道题是 CF1249D1 Too Many Segments (easy version) 的加强版,建议读者先去做一下此题,或是看一下它的题解,理解这两道题 贪心 的基本思路,以免看得一头雾水。

思路

额,本来想略过的,楼上 dalao 讲的很清楚了,但考虑到这是我洛谷上的第一篇题解,还是讲一讲吧。
题目要求我们删除一部分线段使得没有“坏点”的存在,那我们肯定要遍历一遍区间,找到哪个点是“坏点”后,才能进行操作。

当我们枚举到一个点时,如果这个点是“坏点”,那它肯定被多条线段所覆盖,枚举到这个点时,它前面的点一定都不是“坏点”(本来就不是“坏点”或在前面已经删掉一些线段使它不是“坏点”)。
那么当前覆盖这个点的线段的左端点对其毫无用处,无论删除多少条线段都不会改变这个点左面所有点是不是“坏点”,那我们可以关注右端点

贪心地想,一条线段的右端点越“远”,那它就对更多的点有影响(贡献越大)。
所以,我们应该尽量删除右端点更靠右的线段

细节及实现

实现

细节

int n,k; int tot;//被删除线段的数量 struct node{ int r,id; //r记录区间的右端点 //id记录当前区间的编号 }ans[N];//被删除线段的编号 inline bool operator < (node a,node b){ if(a.r == b.r) return a.id < b.id; return a.r < b.r; //以右端点为关键字 } inline bool cmp(node a,node b){ return a.id < b.id; //以编号为关键字升序排序 } vector <node> a[N]; //a[l]记录以l为左端点的线段的相关信息 set <node> st; //set记录覆盖到当前点的线段

signed main(){

read(n),read(k);
int l,r;
node tmp;
F(1,i,n){
    read(l),read(r);
    tmp.r = r,tmp.id = i;
    a[l].push_back(tmp);
}

F(1,i,200000){
    while(st.size() && (*st.begin()).r < i){//区间的右端点小于当前点,说明这个区间已"过期"
        st.erase(*st.begin());
    }//删除"过期"的区间

    for(int j = 0;j < a[i].size();j ++){
        st.insert(a[i][j]);
    }//把以当前遍历到的点为左端点的区间插入至set

    while(st.size() > k){//如果这个点是坏点
        ans[++ tot] = *st.rbegin();//记录要被删除的区间
        st.erase(*st.rbegin());//删除区间
    }
}
printf("%lld\n",tot);
sort(ans + 1,ans + tot + 1,cmp);
F(1,i,tot) printf("%lld ",ans[i].id);

return 0;

}


## 最后的最后
本蒟蒻第五次申请题解,希望管理大大高抬贵手,把审核通过了吧~  
第一次被打回:2024-06-13 10:08  
第二次被打回:2024-07-05 14:18  
第三次被打回:2024-07-08 15:47  
第四次被打回:2024-07-10 22:21