Codeforces Round #523 (Div. 2) D. TV Shows(multiset+思維)
題目連結:
題意:
有 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;
}