POJ 1151 Atlantis(线段树 + 扫描线)

Posted

tags:

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

转载请注明原文:http://www.cnblogs.com/burning-flame/p/5934653.html

题目链接:http://poj.org/problem?id=1151

 

题意:

  给你 n 个矩形的对角线坐标,求 n 个矩形并集的面积。

做法:

  扫描线 + 线段树。

  因为作线段树的题遇到了扫描线,只是粗浅的了解了一下。

  就像字面上的:线性扫描一遍,对于每个单元,因为某些事件的发生会有一些变化。

  举个例子:

    现有长度为 n 的数组a,同时有 n 个区间覆盖[li, ri],对于每个区间,它会把对应区间的数组元素 +1,问最后数组中元素值?

    可以把每个覆盖看为一个事件,一个li代表一个覆盖事件的开始,同样一个ri代表一个覆盖事件的结束。这样,对于这个例子,就是线性对数组元素扫描一遍。元素ai被覆盖次数 = ai-1被覆盖的次数 + 覆盖事件从 i 开始的数目 - 覆盖事件从 i-1 结束的数目(不会影响到元素 i)。

  所以代码如下:

  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <set>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define lson (i<<1)
#define rson (lson|1)
using namespace std;

typedef long long LL;
const int N = 207;
const int INF = 0x7fffffff;

map<int, int> mpL;
map<int, int> mpR;

int main()
{
    int num[N];
    int n;
    while (scanf("%d", &n) != EOF)
    {
        mpL.clear();
        mpR.clear();
        memset(num, 0, sizeof(num));
        for (int i = 0; i < n; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            mpL[a]++;
            mpR[b]++;
        }
        num[1] = mpL[1];
        for (int i = 2; i <= n; i++)
            num[i] = num[i-1] + mpL[i] - mpR[i-1];
        for (int i = 1; i <= n; i++)
            printf("%d%c", num[i], i == n ? \\n :  );
    }
    return 0;
}

 

 

 

而对于这道题:

  推荐博客(图画的很清晰):Coder and Writer的博客

  思路:

    对于x,y轴,选一个方向建线段树(假设以 x 轴建树,那么以矩形的竖线 x 值离散化处理建树,把矩形的横线看作一个个覆盖事件,以 y 值从小到大排序。)

    选另一个方向扫描,对于每一个单元,会有若干事件的发生(使用flag,对于一个矩形的两个横线,靠近 x 轴的横线的flag赋值为 1 代表覆盖,另一个的flag赋值为-1则代表删除之前覆盖的线),在一个单元内发生若干事件同时更新线段树 x 轴被覆盖的长度(sum[1]即是),再乘以当前单元对应的长度即为扫面单元对应并集的面积,扫描过程中累加即可。

  代码实现:

    离散化。 dis数组统计 x 轴上轴任意两条竖线间距。

    cover数组的使用很巧妙。代表当前区间[l, r]被覆盖次数。若当前区间有被 事件 覆盖,之间初始化为对应 x 轴的间距即可,然后向上push_up到根。而当当前区间的所有覆盖被删除时,其子区间可能仍然有覆盖,当当前区间有覆盖时,因为直接初始化并且push_up所以跟子区间的覆盖没有关系。这样就达到了求被覆盖的并集的效果。

 

代码:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <set>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define lson (i<<1)
#define rson (lson|1)
using namespace std;

typedef long long LL;
const int N = 207;
const int INF = 0x7fffffff;

struct line
{
    double y, x1, x2;
    int flag;
}ln[N<<2];

int n, cnt, cnt_ln, cover[N<<2];
double sum[N<<2], dis[N];
map<double, int> mp, mpy;

int cmp(struct line a, struct line b)
{
    return a.y < b.y;
}

void push_up(int L, int R, int i)
{
    if (cover[i])
        sum[i] = dis[R] - dis[L-1];
    else if (L == R)
        sum[i] = 0;
    else
        sum[i] = sum[lson] + sum[rson];
}

void update(int L, int R, int l, int r, int val, int i)
{
    if (L == l && R == r)
    {
        cover[i] += val;
        push_up(L, R, i);
        return;
    }
    int mid = (L + R) >> 1;
    if (r <= mid)
        update(L, mid, l, r, val, lson);
    else if (l > mid)
        update(mid + 1, R, l, r, val, rson);
    else
    {
        update(L, mid, l, mid, val, lson);
        update(mid + 1, R, mid + 1, r, val, rson);
    }
    push_up(L, R, i);
}

int main()
{
    //freopen("in.ads", "r", stdin);
    int cas = 0;
    while (scanf("%d", &n) != EOF && n)
    {
        double ans = 0;
        cnt = 0; dis[0] = 0;
        cnt_ln = 0;
        mp.clear();
        mpy.clear();
        memset(cover, 0, sizeof(cover));
        memset(sum, 0, sizeof(sum));
        for (int i = 0; i < n; i++)
        {
            double x1, x2, y1, y2;
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            ln[cnt_ln].y = y1; ln[cnt_ln].x1 = x1; ln[cnt_ln].x2 = x2; ln[cnt_ln].flag = 1;
            cnt_ln++;
            ln[cnt_ln].y = y2; ln[cnt_ln].x1 = x1; ln[cnt_ln].x2 = x2; ln[cnt_ln].flag = -1;
            cnt_ln++;
            mp[x1] = 1;
            mp[x2] = 1;
            mpy[y1]++;
            mpy[y2]++;
        }
        for (map<double, int>::iterator it = mp.begin(); it != mp.end(); it++)
        {
            it->second = ++cnt;
            map<double, int>::iterator it1 = ++it;
            it--;
            if (it1 != mp.end())
                dis[cnt] = dis[cnt-1] + it1->first - it->first;
        }
        sort(ln, ln + cnt_ln, cmp);
        int idx = 0;
        for (map<double, int>::iterator it = mpy.begin(); it != mpy.end(); it++)
        {
            int cnt1 = 0;
            for (; idx < cnt_ln && cnt1 < it->second; idx++)
            {
                update(1, cnt - 1, mp[ln[idx].x1], mp[ln[idx].x2] - 1, ln[idx].flag, 1);
                cnt1++;
            }
            if (it != --mpy.end())
            {
                map<double, int>::iterator it1 = ++it;
                it--;
                ans += sum[1] * (it1->first - it->first);
            }
        }
        printf("Test case #%d\\nTotal explored area: %.2lf\\n\\n", ++cas, ans);
    }
    return 0;
}

 

以上是关于POJ 1151 Atlantis(线段树 + 扫描线)的主要内容,如果未能解决你的问题,请参考以下文章

POJ 1151 Atlantis(线段树 + 扫描线)

POJ 1151 Atlantis 线段树+离散化

poj1151 Atlantis——扫描线+线段树

poj1151 Atlantis (线段树+扫描线+离散化)

poj-1151-Atlantis-线段树求面积并

POJ1151Atlantis 矩形面积并[线段树 离散化 扫描线]