题目链接:
题意:
有 n 个电视节目,播放的时间区间为 [li,ri] 。同一时间,不同的节目不能在同一台电视上播放。一个节目必须完整的在一台电视上播放完。现在租一台电视需要先付 x 块钱,之后每分钟要付 y 块钱,即租一台电视从时间区间 [a,b] 需要付 x + (b-a)*y 块钱。问看完所有的节目至少需要多少钱。
思路:
把所有节目按开始时间升序排序,若开始时间相同,按结束时间升序排序。排序后依次遍历所有节目,对于节目 i,去找之前的节目中结束时间小于节目 i 的开始时间的,且离的最近的,若两者的时间差*y<=x,那么两者可以合并(即在同一电视上播放),否则,需要新租一个电视。
如何找之前的节目中结束时间小于节目 i 的开始时间的,且离的最近的?首先想到二分查找,但结束时间并不是有序的,因此想到set(会自动排序)。但本题set不行,因为set会自动去重,这会导致错误答案。因此使用multiset,它与set的唯一区别就是不会自动去重。
关于思路的一些证明:
1. 所有节目必须先按开始时间升序排序,不能先按结束时间升序排序:
例子:
———— ——————
——
2. 如下那种选择更优?(假设都是可以合并的)
—— —— | —— ————————
—————— —————— | —————— ——
其实这两种情况是一样的,即间隔之和相同,可自己画图试试。
3. 如下那种选择更优?
———— ———— (不能合并) | ———— ———————(可以合并)
—————— —————— (可以合并) | —————— ———— (可以合并)
这其实和2同理,因为两者的间隔之和相同,所以后者相当于前者两个都合并了,所以后者多付的费用肯定大于前者。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
const ll mod = 1e9+7;
struct Point{
ll l,r;
//定义 < ,用于lower_bound的排序
bool operator < (const Point &a) const {
if(r==a.r){
return l<a.l;
}
return r<a.r;
}
};
int n;
ll x,y;
Point op[MAX];
multiset<Point>s;
bool cmp(Point p,Point q){
if(p.l==q.l){
return p.r<q.r;
}
return p.l<q.l;
}
int main()
{
scanf("%d%lld%lld",&n,&x,&y);
for(int i=0;i<n;i++){
scanf("%lld%lld",&op[i].l,&op[i].r);
}
sort(op,op+n,cmp);
multiset<Point>::iterator pos;
for(int i=0;i<n;i++){
//w.l需要<=0,这样pos--才是我们想要的结果
Point w = Point{0,op[i].l};
pos = s.lower_bound(w);
if(pos==s.begin()){
s.insert(op[i]);
}
else{
pos--;
Point tmp = *pos;
if((op[i].l-tmp.r)*y>x){
s.insert(op[i]);
}
else{
tmp.r = op[i].r;
s.erase(pos);
s.insert(tmp);
}
}
}
ll ans=0;
for(pos=s.begin();pos!=s.end();pos++){
Point now = *pos;
ans = (ans+x+(now.r-now.l)*y%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}