hdu 1542 Atlantis 线段树+矩形面积并+离散化点

//根据几篇的代码修改了,自己好看懂点,haha
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
struct node{
    int l,r,cov;
    double len;//cov是覆盖情况,len表示覆盖的长度
}e[222*4];
struct line{//平行于y轴的线段
    double x,up,down;
    int flag;//1表示矩阵左边,-1是右边
}f[222];
int cmp(line a,line b)
{
    return a.x<b.x;
}
double y[222];
void build(int a,int b,int c)//建线段树
{
    e[c].l=a;
    e[c].r=b;
    e[c].len=0;
    e[c].cov=0;
    if(a==b)
        return;
    int mid=(a+b)/2;
    build(a,mid,2*c);
    build(mid+1,b,2*c+1);
}
void update(int a,int b,int c,int flag)//修改覆盖情况cov,以及计算,
{
    if(a==e[c].l&&b==e[c].r)
    {
        if(flag==1)//cov不可能出现负数,因而可以直接判断
        {
            e[c].cov++;
            e[c].len=y[b+1]-y[a];
        }
        else
        {
            e[c].cov--;
            if(e[c].cov==0)
            {
                if(e[c].l==e[c].r)//叶结点
                    e[c].len=0;
                else
                    e[c].len=e[2*c].len+e[2*c+1].len;
            }
        }
        return;
    }
    int mid=(e[c].l+e[c].r)/2;
    if(b<=mid)
        update(a,b,2*c,flag);
    else if(a>mid)
        update(a,b,2*c+1,flag);
    else
    {
        update(a,mid,2*c,flag);
        update(mid+1,b,2*c+1,flag);
    }
    if(e[c].cov==0)
        e[c].len=e[2*c].len+e[2*c+1].len;
}
int binary(double a[],int l,int r,double x) //二分查找x,并返回为序号   O(logN)
{
    int mid;
    while(l<=r)          
    {
        mid = (l+r)>>1;
        if(x < a[mid])
            r = mid -1;
        else
        {
            if(x > a[mid])
                l = mid +1;
            else
                return mid;
        }
    }
}
int main()
{
    int n,tt=0;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
            break;
        int i,j,k,t=0;
        double x1,y1,x2,y2;
        for(i=0;i<n;i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[t]=y1;f[t].x=x1;f[t].up=y2;f[t].down=y1;f[t++].flag=1;
            y[t]=y2;f[t].x=x2;f[t].up=y2;f[t].down=y1;f[t++].flag=-1;
        }
        sort(y,y+t);//将y1和y2的值离散化成0到t-1的值
        sort(f,f+t,cmp);//用结构体数组f记录每条平行于y轴的线段
        build(0,t-1,1);
        double ans=0;
        for(i=0;i<t;i++)
        {
            if(i!=0)
                ans+=(f[i].x-f[i-1].x)*e[1].len;
            int l=binary(y,0,t-1,f[i].down);//二分查找离散化对应值
            int r=binary(y,0,t-1,f[i].up)-1;//线段长度,由于线段树处理的是点,所以每点表示y[i+1]-y[i]段,所以线段最后点要-1
            update(l,r,1,f[i].flag);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++tt,ans);
    }
    return 0;
}
/*
举例:
数据
2
10 10 20 20
15 15 25 25.5
0
无限延长所有平行于Y轴的线,可得到总面积就是三个矩形((10,10),(15,20)),((15,10)(20,25.5)),((20,15),(25,25.5))面积之和
离散化各点的纵坐标并排序,得到y[0]=10,y[1]=15,y[2]=20,y[3]=25.5;
构建0到3的线段树,其中cov是覆盖情况。从横坐标小的开始枚举所有平行于y轴的线段,如果是矩形左边就cov+1,否则cov-1
这样我们可以知道cov不为0的就是上述三个矩形的宽,长就是两个两个横坐标的差
*/
/*
转载原理:
【题意】:
线段树经典应用,求矩形并的面积
【分析】:
第一道矩形几何题,主要是练习线段树来的
 
求矩形的并,由于矩形的位置可以多变,因此矩形的面积一下子不好求
这个时候,可以采用“分割”的思想,即把整块的矩形面积分割成几个小矩形的面积,然后求和就行了
这里我们可以这样做,把每个矩形投影到 y 坐标轴上来
然后我们可以枚举矩形的 x 坐标,然后检测当前相邻 x 坐标上 y 方向的合法长度,两种相乘就是面积
 
然后关键就是如何用线段树来维护那个 “合法长度”
可以这样来搞
线段树的节点这样定义
struct node { int left,right,cov; double len; }
cov 表示当前节点区间是否被覆盖,len 是当前区间的合法长度
 
然后我们通过“扫描线”的方法来进行扫描
枚举 x 的竖边,矩形的左边那条竖边就是入边,右边那条就是出边了
然后把所有这些竖边按照 x 坐标递增排序,每次进行插入操作
由于坐标不一定为整数,因此需要进行离散化处理
每次插入时如果当前区间被完全覆盖,那么就要对 cov 域进行更新
入边 +1 出边 -1
更新完毕后判断当前节点的 cov 域是否大于 0
如果大于 0,那么当前节点的 len 域就是节点所覆盖的区间
否则
如果是叶子节点,则 len=0
如果内部节点,则 len=左右儿子的 len 之和
转自---------http://blog.sina.com.cn/s/blog_88705ca20100ufl0.html
*/

你可能感兴趣的:(线段树,离散化,矩形面积并)