HDU1255-覆盖的面积(扫描线求面积并)

覆盖的面积

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6599 Accepted Submission(s): 3366

Problem Description
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.

Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.

注意:本题的输入数据较多,推荐使用scanf读入数据.

Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.

Sample Input
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1

Sample Output
7.63
0.00
题目:HDU1255
题意:中文题意。
思路:才做完裸的扫描线,这道题算是一个小小的拓展吧(不会裸的扫描线戳:HDU1542题解)。
题目要求求矩形覆盖过至少两次的区域的面积。最初的想法是在裸的扫描线上记录覆盖的次数,配合线段树的懒惰更新去解题。写完后,跑完样例,WA了。。。
后来发现错误所在:
样例1
input
1
4
2 1 3 10
1 2 8 10
4 11 6 12
5 13 7 14
output
8.00
lazy标记会把当前节点的标记先pushdown到两个子节点,等到下次搜索到子节点时再继续下传,对于上面这个样例,我们先会获得(2,3)这个x坐标集的区间,然后我们去线段树里标记(根据区间左开右闭的性质,我们在线段树里搜索的区间实际是[2,2],不明白戳HDU1542题解),标记大致草图如下,在第5行:
这里写图片描述
第二条线我们获得(1,8)这个x坐标集,然后去线段树里标记[1,8](实际是[1,7],假装[1,8]好了),标记位置如图,在第2行:
这里写图片描述
这时候实际上已经有覆盖区间了,但是我们从根节点去找时,并没有发现标记为2的区间,得到的答案是0,显然是不对的,也就是说,当我们用懒惰更新先更新小区间,再更新大区间时,就有可能会出现错误。
也许是我懒惰更新写的不对,反正博主最后放弃了这个思路。


废话说完,现在是正解思路。
对于每个节点,我们记录两个值cover(覆盖长度),line(不考虑覆盖时,扫描线的长度)。
假设一个区间[l,r]的vis为1那么对于[l,r]这个区间来说,它的覆盖长度cover=leftsong.line+right.line
即当前区间[l,r]存在一条线,这条线必定和当前区间的扫描线相覆盖。
[l,r]的vis为2说明当前区间被覆盖了两次,那么cover=posx[r+1]-posx[l] (区间长度)=line.

AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<math.h>
#define met(s,k) memset(s,k,sizeof s)
#define scan(a) scanf("%d",&a)
#define scanl(a) scanf("%lld",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannl(a,b) scanf("%lld%lld",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define prin(a) printf("%d\n",a)
#define prinl(a) printf("%lld\n",a)
using namespace std;
typedef long long ll;
const int maxn=1e4+100;
int cont,num;
double posx[maxn];
struct Tree
{
    long double cover,line;
    int vis;
}tree[4*maxn];
struct Edge
{
    double xl,xr,h;
    int flag;
    Edge(){};
    Edge(double a,double b,double c,int d) : xl(a),xr(b),h(c),flag(d){}
    bool operator< (const Edge &a)const{
        return h<a.h;
    }
}edge[maxn];
void pushup(int l,int r,int root)
{
    if(tree[root].vis)tree[root].line=posx[r+1]-posx[l];
    else if(l==r)tree[root].line=0;
    else tree[root].line=tree[root*2].line+tree[root*2+1].line;
    if(tree[root].vis>1)tree[root].cover=tree[root].line;//区间被标记两次,覆盖值为区间的长度
    else if(tree[root].vis)tree[root].cover=tree[root*2].line+tree[root*2+1].line;//区间被标记一次,覆盖长度等于区间扫描线长度
    else if(l==r)tree[root].cover=0;
    else tree[root].cover=tree[root*2].cover+tree[root*2+1].cover;
}
void update(int nl,int nr,int add,int l,int r,int root)
{
    if(nl==l&&nr==r)
    {
        tree[root].vis+=add;
        pushup(nl,nr,root);
        return ;
    }
    int mid=(l+r)/2;
    if(nr<=mid)update(nl,nr,add,l,mid,root*2);
    else if(nl>mid)update(nl,nr,add,mid+1,r,root*2+1);
    else update(nl,mid,add,l,mid,root*2),update(mid+1,nr,add,mid+1,r,root*2+1);
    pushup(l,r,root);
}
int main()
{
    int n,t;
    scan(t);
    while(t--)
    {
        cont=0;
        num=1;
        scan(n);
        for(int i=0;i<n;i++)
        {
            double x,y,z,m;
            scanf("%lf%lf%lf%lf",&x,&y,&z,&m);
            edge[cont]=Edge(x,z,y,1);
            posx[cont++]=x;
            edge[cont]=Edge(x,z,m,-1);
            posx[cont++]=z;
        }
        sort(posx,posx+cont);
        sort(edge,edge+cont);
        for(int i=1;i<cont;i++)if(posx[i]!=posx[i-1])posx[num++]=posx[i];
        met(tree,0);
        double ans=0;
        for(int i=0;i<cont-1;i++)
        {
            int l=lower_bound(posx,posx+num,edge[i].xl)-posx;
            int r=lower_bound(posx,posx+num,edge[i].xr)-posx-1;
            update(l,r,edge[i].flag,0,num-1,1);
            ans+=tree[1].cover*(edge[i+1].h-edge[i].h);
        }
        printf("%.2f\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值