HDU 1542(线段树 + 离散化 + 扫描线)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1542

题意: 给你n个矩形的左上角顶点与右下角顶点, 求 n个矩形的面积和(可能重叠,重叠的部分只算一次)。

思路: 扫描线模版题。关于扫描线建议看这个博客: http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html

还有一个比较重要的问题,

一般的线段树以及我们的区间修改合并,都有一个共同点,就是不会出现区间缺失的现象,什么叫区间缺失,顾名思义,区间缺失就是缺少一些区间没有进行运算,这里的扫描线就会遇到这个问题。

普遍的,我们的线段树以及数据区间分布是这样的:

[1, a][a + 1, b][b + 1, c][c + 1, d][d + 1, e].......

但是如果只是简简单单的用这个来解决扫描线的问题会导致错误,为什么因为,他没有涉及到[a,a + 1],在扫描线中会出现[a,a + 1]中的数据,而常用的线段树的区间概念是无法解决这样的问题的,出现了所谓的区间缺失,怎样解决,下面的代码给出了解决方案,这里简单的提一下,就是利用[ , ),这个区间性质,左闭右开,即可解决区间缺失问题

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 210;
struct xx{      ///扫描线
    double l,r,h;  ///左端点,右端点与高度
    int d;      ///标号1或 -1,表明是下边还是上边
    xx(){}
    xx(double l,double r,double h,int d):l(l),r(r),h(h),d(d){}
}line[maxn << 4];

double sum[maxn << 4];              ///存放投影
double x[maxn << 4];                ///存放所有的x坐标
int cnt[maxn << 4];                 ///cnt[i] != 0 说明 i代表的 这个区间 被覆盖, == 0则未被覆盖或未被完全覆盖
                                    ///(对于一个区间上下边一定是成对出现,扫描线从下往上扫,途中cnt的值可能大于1,但一定不可能小于0)
bool cmp(xx A, xx B){
    return A.h < B.h;
}

void pushup(int l,int r,int i){
    if(cnt[i]) sum[i] = x[r+1] - x[l];          /// cnt != 0 被覆盖,计算出这一段的长度, [ ) 避免区间缺失
    else if(l == r) sum[i] = 0;                 /// 显然, 此时长度为 0
    else sum[i] = sum[i<<1] + sum[i<<1|1];      /// 未被完全覆盖,(最后是push up 到sum[1]),所以 = 左右儿子的sum 之和
}

void build(int l,int r,int i){                 /// 此建树操作 可用两个memset 代替
    int mid = (l + r) >> 1;
    sum[i] = 0;
    cnt[i] = 0;
    if(l == r) return ;
    build(l,mid,i << 1);
    build(mid + 1,r,i << 1 | 1);
  //  pushup(l,r,i);
}

void update(int l,int r,int ul,int ur,int i,int key){
    if(ul <= l && r <= ur){                                 ///当前到达的区间 已经被包含在需要更新的区间中, 直接更新cnt , 然后pushup
        cnt[i] += key;
        pushup(l,r,i);
    }else {
        int mid = (l + r) >> 1;
        if(ul <= mid) update(l,mid,ul,ur,i << 1,key);
        if(mid < ur) update(mid + 1,r,ul,ur,i << 1 | 1,key);
        pushup(l,r,i);
    }
}

int main()
{
    int n,k = 1;
    while(~scanf("%d",&n)){
        if(!n) break;
        int pl = 1;
        double x1,y1,x2,y2;
        for(int i = 1;i <= n;i ++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[pl] = xx(x1,x2,y1,1);
            x[pl++] = x1;
            line[pl] = xx(x1,x2,y2,-1);
            x[pl++] = x2;
        }
        ///离散化
        sort(line + 1,line + pl,cmp);
        sort(x + 1,x + pl);
        int lisan = unique(x + 1,x + pl) - x - 1;
        build(1,lisan, 1);
        double ans = 0;
        for(int i = 1;i < pl;i ++){
            int l = lower_bound(x+1,x+lisan+1,line[i].l) - x;
            int r = lower_bound(x+1,x+lisan+1,line[i].r) - x - 1;///[ ) 的处理,注意这里的r有一个减一操作,这就是上面r+1的原因,保证区间左闭右开,这样可以防止区间缺失 

            update(1,lisan,l,r,1,line[i].d);
            ans += sum[1] * (line[i+1].h - line[i].h);
        }
        printf("Test case #%d\n",k++);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/no_O_ac/article/details/81221507
今日推荐