关于扫描线的一些理解

关于扫描线这个东西,其实是不太好开始学习的,因为百度到的东西大部分是比较高深,或者是和计算几何相关的东西。
感觉稍微写点自己的理解吧……


一、关于扫描线


关于扫描线这个东西,其实就和我们脑海中想象的是一样的,一根线在要进行的区间上进行“扫描”。
扫到的地方就在线上进行某些性质的改变。
所以扫描线更像是一种思想,而不是某些特定的算法。


二、扫描线的初级实现


拿一道例题来看。
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这个题就是利用了线段树来维护“事件”。

(未完待补)










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