HDU 1542

我们不难发现,我们只需要保存这张图里面的所有水平的边即可。

对于每条边,它所拥有的属性是:这条边的左右端点(的横坐标),这条边的高度(纵坐标),这条边属于矩形的上边还是下边

刚刚计算中我们遇到两个蓝色矩形的一部分一眼就能看出这两个蓝色矩形的‘宽’是多少,用计算机怎么做到?

线段树

我们以整个图最左边的竖线作为区间左端点,最右边的竖线作为区间右端点,去维护这个区间的有效长度(即被覆盖的长度

比如扫到第2条边的时候,有效长度就是两个蓝色矩形的宽之和。

这样,我们用扫描线去扫描每一条边的时候,都需要更新线段树的有效长度

是如何更新的呢?

如果扫到的这条边是某矩形的下边,则往区间插入这条线段

如果扫到的这条边是某矩形的上边,则往区间删除这条线段

为什么?自己试着模拟一下就不难发现:

因为我们是自下而上的扫这个图,扫到下边相当于刚刚进入一个矩形,扫到上边则是要离开一个矩形

利用线段树把每条边的有效长度找到了,也就是找到了每部分的所有矩形的总宽,那么高呢?

高就简单多了,对于所有的边,按照高度从小到大排列,那么矩形高就是每相邻边之间的高度差

由于横坐标太大,这里我们离散化一下,另外插入边的时候,右端点要减一目的是为了防止有一个区间在线段树的两个子树上时,会少计算左子树右端点和右子树左端点之间的那段距离,因此,对线段树的定点定义为区间才行,而且更好处理;

另外,我们知道这个lazy标记不是覆盖也不是叠加,看图即可明白,因此当我们搜索区间的时候,只要搜到标记不是零那么肯定对宽有贡献,如果是零,那么就要看,他的子树,子树之前被覆盖但是却不一定不是一,因此,只需要求两个子树和就好:

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define L l , m , u << 1
#define R m + 1 , r , u << 1 | 1
typedef long long LL;
const int N=111;
double x[2*N],len[N<<3];
int add[N<<3];
struct Edge
{
    double l,r,h;int f;
    Edge(double _l=0,double _r=0,double _h=0,int _f=0):l(_l),r(_r),h(_h),f(_f){}
    bool operator <(const Edge &r)const{return h<r.h;}
}e[N<<1];

void PushUp(int u,int l,int r)//把当前结点的信息更新到父结点
{
    if(add[u])len[u]=x[r+1]-x[l];
    else if(l==r)len[u]=0.0;
    else    len[u]=len[u<<1]+len[u<<1|1];
}

void build(int l,int r,int u)
{
    add[u] = 0;
    len[u] = 0.0;
    if (l == r)
    {
        return;
    }
    int m = (l + r) >> 1;
    build(L);
    build(R);
}

void update(int l1,int r1,int c,int l,int r,int u)
{
    if (l1 <= l && r <= r1)
    {
        add[u] += c;
        PushUp(u,l,r);
        return ;
    }
    int m = (l + r) >> 1;
    if (l1 <= m)
        update(l1 , r1 , c , L);
    if (m < r1)
        update(l1 , r1 , c , R);
    PushUp(u,l,r);
}

int main()
{
    freopen("input.txt","r",stdin);
    int n;int cas=0;
    while(scanf("%d",&n)!=EOF&&n)
    {
        int tot=0;
        for(int i=0;i<n;i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[tot]=x1,e[tot++]=Edge(x1,x2,y1,1);
            x[tot]=x2,e[tot++]=Edge(x1,x2,y2,-1);
        }
        sort(e,e+tot);
        sort(x,x+tot);
        int k=1;
        for(int i=1;i<tot;i++)
            if(x[i]!=x[i-1])
                x[k++]=x[i];
        build(0,k-1,1);
        double ans=0.0;
        for(int i=0;i<tot;i++)
        {
            int l = lower_bound(x,x+k,e[i].l) - x;
            int r = lower_bound(x,x+k,e[i].r) - x-1;
            update(l,r,e[i].f,0,k-1,1);
            ans+=(e[i+1].h - e[i].h)*len[1];
        }
        printf("Test case #%d
",++cas);
        printf("Total explored area: %.2f

",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/MeowMeowMeow/p/7308434.html