(使用线段树实现的)扫描线算法

Posted 六花的邪王真眼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(使用线段树实现的)扫描线算法相关的知识,希望对你有一定的参考价值。

(使用线段树实现的)扫描线算法

一、算法应用场景

  一个空间中存在若干矩形,且矩形的放置方向一致——(矩形的每条边必然和X或者Y轴平行)

  求这些矩形覆盖的总面积的大小。(存在若干个矩形相互重叠的问题)

 

二、解决思路

  考虑线段树可以logN的时间内做到区间覆盖,区间设置特殊值。因此应当采用线段树进行计算。

  考虑使用线段树维护当前X轴的长度,则面积应当为:Σlength*hight。其中hight为当前Y轴长度减去上一个Y轴长度。length为当前维护的结果。

  考虑当将矩形切割成两条形如,x1,x2,y,val的命令,其中x1,x2代表当前上线/下线的长度;y代表当前高度,val取1或-1分别带至矩形的起始边和结束边。

 

三、代码实现

  对于当前应用场景,具有很明显的特点,及,对于任意一个矩形,都必然将初始边和结束边加进线段树中。则若某给定区间为a,b时,不存在当a,b边对应的结束边还未被加进线段树时就被清零或部分清零的可能性。

  因此,对于任意区间,都有显而易见的两种状态:

    1,明确的知道该区间已经被填满。——则区间长度为区间所能表示的上下限之差。

    2,不知道该区间是否被填满。——则区间长度为两个子区间之差。

  对于2情况有一个子状态:及当前区间仅仅包含一个元素:(a == b-1) ——我代码中区间定义为左闭右开。对于该区间,若无明确的增加指令,则应当认为区间长度为0。

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const ll MAXN= 1<<19;
const ll MAXM = 2333;
int t,n,sizeOfMap =0;;
class Node
{
    public:
        int l,r,number;
        double sum;
        double lf,rf;
};
Node tree[MAXN];

class Command
{
    public:
        double x1,x2,y;
        int val;
        
        const bool operator < (Command const &c)
        {
            return this->y < c.y;
        }
        Command(){}
        Command(double x1,double x2,double y,int v)
        {
            this->x1 = x1;
            this->x2 = x2;
            this->y = y;
            this->val = v;
        }        
};Command commands[MAXM];
int command_number = 0;

void tree_init(int a,int b,int now)
{
    tree[now].l = a;
    tree[now].r = b;
    tree[now].number = 0;
    tree[now].sum = 0;
    if(a == b-1)return;
    int mid = (a+b)/2;
    tree_init(a,mid,now*2);
    tree_init(mid,b,now*2+1);
}

double arr[MAXM];

int find_pos(double tar)
{
    return lower_bound(arr,arr+sizeOfMap,tar)-arr;
}

void update(int a,int b,int now,int key)
{
    int l = tree[now].l;
    int r = tree[now].r;
    int mid = (l+r)/2;
    int num = tree[now].number;
    int lc = now * 2;
    int rc = now * 2 + 1;
    
    if(l == a && r ==b)
    {
        tree[now].number += key;
        if(tree[now].number == 0)
        {
            if(l == r-1)tree[now].sum =0;
            else tree[now].sum = tree[lc].sum + tree[rc].sum;
        }else tree[now].sum = arr[r]-arr[l];
        return ;
    }
    if(a < mid)
    {
        update(a,min(b,mid),lc,key);
        if(b > mid)update(mid,b,rc,key);
    }else update(a,b,rc,key);

    if(tree[now].number == 0)
    {
        if(l == r-1)tree[now].sum =0;
        else tree[now].sum = tree[lc].sum + tree[rc].sum;
    }else tree[now].sum = arr[r]-arr[l];
    return ;
        
    
}
int cases = 1;

void init()
{
    sizeOfMap = 0;
    command_number = 0;
    // cin>>n;
    for(int i=0;i<n;++i)
    {
        // cin>>arr[i];
        double a,b,c,d;
        cin>>a>>b>>c>>d;
        arr[sizeOfMap++] = a;
        arr[sizeOfMap++] = b;
        arr[sizeOfMap++] = c;
        arr[sizeOfMap++] = d;
        commands[command_number++]=Command(a,c,b,1);
        commands[command_number++]=Command(a,c,d,-1);
    }
    sort(arr,arr+sizeOfMap);
    sort(commands,commands+command_number);
    tree_init(0,sizeOfMap+2,1);
    double last = 0.0;
    double ans = 0;
    for(int i=0;i<command_number;++i)
    {
        double line_now = commands[i].y;
        if(line_now != last)
        {
            double hight = line_now - last;
            
            ans += hight * tree[1].sum;
            
            // cout<<tree[1].sum<<ends<<hight<<"last: "<<last<<ends<<line_now<<endl;
            
            last = line_now;
        }
        double x1 = commands[i].x1;
        double x2 = commands[i].x2;
        int val = commands[i].val;
        update(find_pos(x1),find_pos(x2),1,val);
    }
    
    // cout<<ans<<endl;
    printf("Test case #%d\nTotal explored area: %.2f\n\n",cases++,ans);
    
}

int main()
{
    
    // freopen("data_scanner.in","r",stdin);
    cin.sync_with_stdio(false);
    // cin>>t;
    // for(int i=0;i<t;++i)
    while(cin>>n&&n)
    {
        init();
        // cout<<tree[1].sum<<endl;
    }
    
    
    return 0;
}

 

以上是关于(使用线段树实现的)扫描线算法的主要内容,如果未能解决你的问题,请参考以下文章

扫描线算法的介绍与论证

HDU - 1542 扫描线入门+线段树离散化

优先队列 + 并查集 + 字典树 + 树状数组 + 线段树 + 线段树点更新 + KMP +AC自动机 + 扫描线

POJ1151Atlantis 矩形面积并 扫描线 线段树

集训队8月17日

源每一22 还是线段树