感觉稍微写点自己的理解吧……
一、关于扫描线
关于扫描线这个东西,其实就和我们脑海中想象的是一样的,一根线在要进行的区间上进行“扫描”。
扫到的地方就在线上进行某些性质的改变。
所以扫描线更像是一种思想,而不是某些特定的算法。
二、扫描线的初级实现
拿一道例题来看。
Codeforces Educational Rouund #4 D
题意是 以Li,Ri的形式给出一些线段,让你求出恰好被覆盖了k次的区间。
我们在脑内脑补一下。
假设有一条竖直线,从左到右的把整个区间扫描了一遍。
每次遇到一个左端点,覆盖数+1,遇到一个右端点,覆盖数-1。
我们称“覆盖数的变化”为 “事件”。
在这道题里,一条线段对应的是两个事件。
我们定义一个event,记录下每条线段的L和时间的种类——即“入”或“出”。
将“入”记为+1,“出”为-1是比较容易理解的。
在这道题里我们可以发现,事件的发生顺序是由L决定的,所以我们按照L的升序将线段排序。
然后在扫描的过程中我们就可以得到 当覆盖数为k时的区间。
复杂度 O(nlogn) n为事件数(本题事件数为线段数*2)。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 1e10
using namespace std;
int main(){
LL n,k,a,b,cur=0;
scanf("%I64d%I64d",&n,&k);
vector<pair<LL,LL> > event;
vector<LL> ans;
for (LL i=0;i<n;i++) {
scanf("%I64d%I64d",&a,&b);
event.push_back(make_pair(a,-1));
event.push_back(make_pair(b,1));
}
sort(event.begin(),event.end());
int cnt=0;
for (LL x=0;x<event.size();x++){
if (event[x].second==1){
if (cnt==k) ans.push_back(event[x].first);
cnt--;
}
else {
cnt++;
if (cnt==k) ans.push_back(event[x].first);
}
}
printf("%d\n",ans.size()/2);
for (int x=0;x<ans.size()/2;x++)
printf("%I64d %I64d\n", ans[x*2],ans[x*2+1]);
return 0;
}
三、扫描线的中级实现
上面说到扫描线经常可以和一些面积问题相结合,还可以使用某些数据结构来维护。
比如POJ 1151这个题就是利用了线段树来维护“事件”。
(未完待补)