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
题解
矩形面积并的进阶题,其实难度差不多,做这道题之前首先我们要知道怎样用扫描线去求矩形面积并,其实大致思路就是每次用一个平行于x轴的线(扫描线)沿着y轴从小到大走,同时用线段树维护有效线段长度,并用有效长度乘于高度得到面积,因为坐标可以是double类型的,所以我们可以先离散化处理一下。
具体过程参考https://blog.csdn.net/xianpingping/article/details/83032798
然后这个题就好做了,只需要在pushup的时候改一下就行。
回忆一下一般的求矩形面积并,线段树节点里面有一个重要的变量,cnt。这个变量表示了该节点表示的区间被完全覆盖,如果cnt==0,说明没有被完全覆盖(但不代表没有被覆盖),要算出该节点所代表的区间被覆盖的长度,需要由它左右孩子节点被覆盖的长度相加所得。如果cnt==1,表示被完全覆盖,覆盖长度就是该区间长度。如果cnt>1说明也是被完全覆盖,不过不止覆盖了一次,在算覆盖长度的时候,和cnt==1的计算方法是一样的。注意一点,节点里还有另一个变量len,就是该区间被覆盖的长度,但是我们注意一下,这个len准确的意义应该是,被覆盖了一次或以上的长度,只是这个意义在一般的求面积问题中,不需要过分强调。(转自https://www.cnblogs.com/scau20110726/archive/2013/04/14/3020998.html)
这道题目我们需要在线段树里面多维护一个信息:被覆盖两次以上的有效线段长度(len2)。
我们是怎么计算最后的面积的?一样的道理,从下往上扫描矩形,每次添加一条矩形上下边,然后看看tree[1].len2是多少,再乘上高度差。因为tree[1]表示了总区间,而len2表示被覆盖两次或以上的长度,即计算时我们忽略掉只被覆盖一次的长度。
在求节点的len2时我们分成三种情况讨论就行了
当cnt>1时,tree[rt].len2=x[r+1]-x[l],即区间被覆盖了两次以上,长度就是线段长度。
当l==r时,即为叶子节点时,没有儿子节点,所以长度为0,即tree[rt].len2=0;
当不是叶子节点且cnt==1时(至少被完全覆盖了一次),tree[rt].len2=tree[rt<<1].len1+tree[rt<<1|1].len1;
可以这样理解,当前节点至少被完全覆盖了一次,但是否被完全覆盖两次以上不确定,因此我们当前节点的被完全覆盖两次以上的长度就是左右儿子至少被完全覆盖一次的长度之和(因为当前节点原来就被完全覆盖了一次)。
当不是叶子节点且cnt==0时,tree[rt].cnt2=tree[rt<<1].cnt2+tree[rt<<1|1].cnt2.
因为不确定当前节点是否被完全覆盖过,所以长度为儿子节点至少被完全覆盖两次以上的长度之和。
代码实现
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define e 2.718281828 #define rp(i,s,t) for (i = (s); i <= (t); i++) #define RP(i,s,t) for (i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int N = 2e3+7; struct Tree{ int l,r; int cnt; double sum1,sum2;//分别表示被覆盖一次以上和两次以上的有效长度 }tree[N<<2]; struct Node{ int d; double l,r,h; Node(){} Node(double l,double r,double h,int d):l(l),r(r),h(h),d(d){} bool operator <(const Node & a){ return h<a.h; } }p[N]; double x[N]; void pushup(int rt,int l,int r){ if(tree[rt].cnt) tree[rt].sum1=x[r+1]-x[l]; else if(l==r) tree[rt].sum1=0; else tree[rt].sum1=tree[rt<<1].sum1+tree[rt<<1|1].sum1; if(tree[rt].cnt>=2) tree[rt].sum2=x[r+1]-x[l]; else if(l==r) tree[rt].sum2=0; else if(tree[rt].cnt==1) tree[rt].sum2=tree[rt<<1].sum1+tree[rt<<1|1].sum1; else tree[rt].sum2=tree[rt<<1].sum2+tree[rt<<1|1].sum2; } void build(int l,int r,int rt){ tree[rt].l=l,tree[rt].r=r; tree[rt].sum1=tree[rt].sum2=tree[rt].cnt=0; if(l==r) return; int m=(l+r)>>1; build(lson); build(rson); } void update(int rt,int L,int R,int opt){ if(L<=tree[rt].l&&tree[rt].r<=R){ tree[rt].cnt+=opt; pushup(rt,tree[rt].l,tree[rt].r); return ; } int m=(tree[rt].l+tree[rt].r)>>1; if(L<=m) update(rt<<1,L,R,opt); if(m<R) update(rt<<1|1,L,R,opt); pushup(rt,tree[rt].l,tree[rt].r); } int main(){ int T=read(); while(T--){ int q=read(),i; int m=0; int n=0; rp(i,1,q){ double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); p[++m]=Node(x1,x2,y1,1); p[++m]=Node(x1,x2,y2,-1); x[++n]=x1,x[++n]=x2; } sort(x+1,x+1+n); sort(p+1,p+1+m); int k=unique(x+1,x+1+n)-x-1; build(1,k,1); double ans=0; rp(i,0,m-1){ int l=lower_bound(x+1,x+1+k,p[i].l)-x; int r=lower_bound(x+1,x+1+k,p[i].r)-x; r--; if(l<=r) update(1,l,r,p[i].d); ans+=tree[1].sum2*(p[i+1].h-p[i].h); } printf("%.2f\n",ans); } return 0; }
hdu1255——矩形面积并(两次覆盖以上)
猜你喜欢
转载自blog.csdn.net/qq_43472263/article/details/103600645
今日推荐
周排行