扫描线作用 & 算法步骤
扫描线可以讲区间分成不相交的区间,对于每个区间端点,可以利用+fee -fee操作(类似于差分、前缀和思想)来求解一些最优问题。
举栗子:一家餐厅一天会来很多客人,每个客人有一个arriveTime,leaveTime,我们想求出一天中,究竟同时又最多人的时候。
如上区间,可以看到最多人是时候是3个人,先按照arriveTime排个序,然后每次at时都要加一,每次lt时都要减一,最后一用一个变量求最值就行。
下面来一道题目:
题目链接:https://atcoder.jp/contests/abc188/tasks/abc188_d
思路:利用扫描线模型来考虑这个问题;首先不是有区间吗,譬如 1 ~ 2,是有两天的那么这个客人应该是第1天到,第3天离开,共两天(这里其实很难理解,有点类似物理,你12两天,不是有12 , 2~3 两个时间段吗?)。
那好了,区间已经定义好了,我们在L += c ,R -= c,就能够利用上面的模型了。
步骤如下:
1.先把所有区间的所有端点(左端点直接放,右端点+1再放),放到set中(去重)。然后每个端点都要对应的+c 或者-c操作(L 要+ ,R要减)。
2.为了做到上面的端点+-c,我们要做一个对应端点的映射,所以就要用到map做一个映射
3.把set中的端点拿出来,按时间线排序,然后对于每个端点用map找到+-c的操作,结果记录在acc中;用一个res += min(acc,C) * 区间长度(set,和排序其实是相当于够着了一个无重叠的区间,这个区间就要某一/几个服务的时间,acc记录了这个段总共要多少费用)。最后res就是结果
参考代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2 * 1e5 + 10;
set<int> s;
int a[N] ,b[N] , c[N];
map<int,int> ch;
int n,C;
signed main(){
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("tpl.txt","r",stdin);
#endif
cin >> n >> C;
for(int i = 1; i <= n; ++i){
cin >> a[i] >> b[i] >> c[i];
s.insert(a[i]);
s.insert(b[i] + 1);
ch[a[i]] += c[i];
ch[b[i] + 1] -= c[i];
}
vector<int> vc(s.begin(),s.end());
// sort(vc.begin(),vc.end());
int acc = 0;
int res = 0;
for(int i = 0; i < vc.size() - 1; i++){
acc += ch[vc[i]];
res += min(acc,C) * (vc[i+1] - vc[i]);
}
cout << res << endl;
}