poj 1151 Atlantis(多矩形面积) + poj 1177Picture(多矩形周长) 线段树进阶

题意:平面内有很多矩形,求它们组成的图形的总面积;

题解:

先将清楚一个东西,我用的扫描线是一根平行y轴的线,从左到右扫描(你也可以用一根横向的扫描线从下到上扫描),离散化就是把这跟线上的点hash一下;

1: 什么是离散化,其实很简单不要被这三个陌生的字给吓到了,这题我用的是将在y轴的点线离散化 ;

比如有y轴上有4个坐标分别是1, 30, 1000, 100000;按照一般的建树要从 1~100000;

离散化意思就是将于他们hash一下1还是1,30看成是2,1000看成3, 100000看成4,然后建树就变成了1~4;

巨大的节省了时间和空间;

poj  1151求面积这题有坐标是小数,你只能离散化建树;

下面再来看看扫描线:http://www.cnblogs.com/Booble/archive/2010/10/10/1847163.html(很详细,是求的周长);

不想讲了,其实会求周长了,就肯定会求面积了;

先看下这个:

struct TreeLines {
    int nLines;//这个区间里包括的线段的数量
    int lLines;//这个区间左端点是否被覆盖;
    int rLines;//右端点是否被覆盖;
    //上面两个没其他用,就是辅助对nLines的计算的,先别想太多;
    int cover;//当前区间被覆盖的次数,其实也是用来辅助计算的
    int m;//测度(意思就是这个区间里被覆盖线段的总和)
} tree[8*MAXN];


注意:还有一个建树问题,别那样建,要这样建,哈哈哈!

还有就是当要插入或删除的线段x值相等时,要先插入再删除;因为如果两个矩形相邻,重合的那条边是不计入总周长的;

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define lson k<<1, l, mid, ff
#define rson k<<1|1, mid, r, ff
const int MAXN = 5002;
//要插入或删除的线段;
struct Lines {
    int x, y1, y2, flag;//flag=1表示要插入的线段,-1表示要删除的;
    Lines(int a, int b, int c, int d):x(a),y1(b),y2(c),flag(d){}
    Lines(){}
} lines[2*MAXN];
//扫描线(线段树)
struct TreeLines {
    int lLines, rLines, cover, m, nLines;
} tree[8*MAXN];

int yID[2*MAXN];//hash
//更新m
void updataM(int k, int l, int r) {
    if (tree[k].cover > 0)
        tree[k].m = yID[r]-yID[l];
    else {
        if (r-l == 1) tree[k].m = 0;
        else tree[k].m = tree[k<<1].m + tree[k<<1|1].m;
    }
}
//更新nLines,实际上是3个变量一起更新
void updataN(int k, int l, int r) {
    if (tree[k].cover > 0)
        tree[k].lLines = tree[k].nLines = tree[k].rLines = 1;
    else {
        if (r-l == 1)
            tree[k].lLines = tree[k].nLines = tree[k].rLines = 0;
        else {
            int ll = k<<1, rr = k<<1|1;
            tree[k].lLines = tree[ll].lLines;
            tree[k].rLines = tree[rr].rLines;
            tree[k].nLines = tree[ll].nLines+tree[rr].nLines-(tree[ll].rLines&tree[rr].lLines);
        }
    }
}

void query(int li, int ri, int k, int l, int r, int ff) {
    if (li <= yID[l] && ri >= yID[r]) {
        tree[k].cover += ff;
    }
    else if (r-l > 1){//我说是这样建树
        int mid = (l+r)/2;
        if (li < yID[mid]) query(li, ri, lson);
        if (ri > yID[mid]) query(li, ri, rson);
    }
    //这里比较重要是回归的时候更新值;(第一次做这样的)
    updataM(k, l, r);
    updataN(k, l, r);
}

int cmp(Lines aa, Lines bb) {
    if (aa.x == bb.x)
        return aa.flag > bb.flag;//这里注意,先要插入,在删除
    else return aa.x < bb.x;
}

int main() {
    int x1, x2, y1, y2, j = 1, T;
    scanf("%d", &T);
    memset(tree, 0, sizeof(tree));
    while (T--) {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        lines[j] = Lines(x1, y1, y2, 1);
        yID[j++] = y1;
        lines[j] = Lines(x2, y1, y2, -1);
        yID[j++] = y2;
    }
    sort(yID+1, yID+j);
    int k = 1;
    for(int i = 1; i < j; i++) {//删除重复节点
        yID[k++] = yID[i];
        while (yID[i] == yID[i+1]) i++;
    }
    sort(lines+1, lines+j, cmp);
    int res = 0, preN, preM = 0;
    for(int i = 1; i < j; i++) {//一个区间,一个区间的求;
        query(lines[i].y1, lines[i].y2, 1, 1, k-1, lines[i].flag);
        if (i > 1) res += preN*2*(lines[i].x-lines[i-1].x);
        res += abs(preM-tree[1].m);
        preM = tree[1].m;
        preN = tree[1].nLines;
    }
    printf("%d\n", res);
    return 0;
}

求面积的话,就只要更新测度m了;

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define lson k<<1, l, mid, ff
#define rson k<<1|1, mid, r, ff
const int MAXN = 102;

struct Lines {
    float x, y1, y2;
    int flag;
    Lines(float a, float b, float c, int d):x(a),y1(b),y2(c),flag(d){}
    Lines(){}
} lines[2*MAXN];

struct TreeLines {
    int cover;
    float m;
} tree[8*MAXN];

float yID[2*MAXN];

void updataM(int k, int l, int r) {
    if (tree[k].cover > 0)
        tree[k].m = yID[r]-yID[l];
    else {
        if (r-l == 1) tree[k].m = 0;
        else tree[k].m = tree[k<<1].m + tree[k<<1|1].m;
    }
}

void query(float li, float ri, int k, int l, int r, int ff) {
    if (li <= yID[l] && ri >= yID[r]) {
        tree[k].cover += ff;
    }
    else if (r-l > 1){
        int mid = (l+r)/2;
        if (li < yID[mid]) query(li, ri, lson);
        if (ri > yID[mid]) query(li, ri, rson);
    }
    updataM(k, l, r);
}

int cmp(Lines aa, Lines bb) {
    if (aa.x == bb.x)
        return aa.flag > bb.flag;
    else return aa.x < bb.x;
}

int main() {
    float x1, x2, y1, y2;
    int j, T, tt = 1;
    while(scanf("%d", &T) && T) {
        memset(tree, 0, sizeof(tree));
        j = 1;
        while (T--) {
            scanf("%f%f%f%f", &x1, &y1, &x2, &y2);
            lines[j] = Lines(x1, y1, y2, 1);
            yID[j++] = y1;
            lines[j] = Lines(x2, y1, y2, -1);
            yID[j++] = y2;
        }
        sort(yID+1, yID+j);
        int k = 1;
        for(int i = 1; i < j; i++) {
            yID[k++] = yID[i];
            while (yID[i] == yID[i+1]) i++;
        }
        sort(lines+1, lines+j, cmp);
        float preM = 0;
        double res = 0;
        for(int i = 1; i < j; i++) {
            query(lines[i].y1, lines[i].y2, 1, 1, k-1, lines[i].flag);
            if (i > 1) res +=  preM*(lines[i].x-lines[i-1].x);
            preM = tree[1].m;
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", tt++, res);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值