關於掃描線的一些理解

關於掃描線這個東西,其實是不太好開始學習的,因爲百度到的東西大部分是比較高深,或者是和計算幾何相關的東西。
感覺稍微寫點自己的理解吧……


一、關於掃描線


關於掃描線這個東西,其實就和我們腦海中想象的是一樣的,一根線在要進行的區間上進行“掃描”。
掃到的地方就在線上進行某些性質的改變。
所以掃描線更像是一種思想,而不是某些特定的算法。


二、掃描線的初級實現


拿一道例題來看。
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這個題就是利用了線段樹來維護“事件”。

(未完待補)










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章