感覺稍微寫點自己的理解吧……
一、關於掃描線
關於掃描線這個東西,其實就和我們腦海中想象的是一樣的,一根線在要進行的區間上進行“掃描”。
掃到的地方就在線上進行某些性質的改變。
所以掃描線更像是一種思想,而不是某些特定的算法。
二、掃描線的初級實現
拿一道例題來看。
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這個題就是利用了線段樹來維護“事件”。
(未完待補)