/* 在这里将一个一个的矩形分隔成两条平行X轴的线, 在下面一条标记为正边,上面一条标记为负边。 cnt表示的次节点被覆盖的次数。 sum表示区间一次覆盖以上的长度 len表示区间二次覆盖以上的长度 */ #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define maxn 2222 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid (l+r)>>1 double X[maxn<<2],sum[maxn<<2],len[maxn<<2]; int cnt[maxn<<2]; struct Seg{ double l,r,h; int c; Seg(){}; Seg(double t1,double t2,double t3,int t4) :l(t1),r(t2),h(t3),c(t4){} bool operator < (Seg& cmp){ return h<cmp.h; } }seg[maxn<<2]; void Pushup(int l,int r,int rt){ if(cnt[rt]) sum[rt] = X[r+1]-X[l];//实际长度时往开区间移 else if(l == r) sum[rt] = 0; else sum[rt] = sum[rt<<1]+sum[rt<<1|1]; } void Pushupagain(int l,int r,int rt){ if(cnt[rt]>=2) //如果已经两次以上被覆盖,即为交集 len[rt] = X[r+1] -X[l]; else if(l == r)//叶子节点长度为0 len[rt] = 0; else if(cnt[rt] == 1)//如果覆盖线段为1,那么等于已经覆盖的左右子树的长度和 len[rt] = sum[rt<<1] +sum[rt<<1|1]; else //否则等于已经覆盖的左右子树长度和。 len[rt] = len[rt<<1] + len[rt<<1|1]; } int Bin(double key,int n){ return lower_bound(X,X+n,key)-X; } void Update(int L,int R,int c,int l,int r,int rt){ if(L<=l && r<=R){ cnt[rt]+=c; Pushup(l,r,rt); Pushupagain(l,r,rt); return ; } int m=mid; if(m>=L) Update(L,R,c,lson); if(m<R) Update(L,R,c,rson); Pushup(l,r,rt); Pushupagain(l,r,rt); } int main(){ int t,i,j,T; scanf("%d",&T); while(T--){ scanf("%d",&t); double a[4]; int n=0; for(i=0;i<t;i++){ for(j=0;j<4;j++) scanf("%lf",&a[j]); seg[n]=Seg(a[0],a[2],a[1],1); X[n++]=a[0]; seg[n]=Seg(a[0],a[2],a[3],-1); X[n++]=a[2]; } sort(seg,seg+n); sort(X,X+n); int k=1; for(i=1;i<n;i++){ //去重离散化 if(X[i-1]!=X[i]) X[k++]=X[i]; } memset(cnt,0,sizeof(cnt)); memset(sum,0,sizeof(sum)); memset(len,0,sizeof(len)); double area=0,sre=0; for(i=0;i<n-1;i++){ int l=Bin(seg[i].l,k); int r=Bin(seg[i].r,k)-1; //左闭右开区间,取实区时往后移一个 if(l<=r) Update(l,r,seg[i].c,0,k-1,1); area+=len[1]*(seg[i+1].h-seg[i].h); //sre+=sum[1]*(seg[i+1].h-seg[i].h); } // printf("Test case #%d\nTotal explored area: %.2lf\n\n",++cas,area); printf("%.2lf\n",area); //area 为面积交,sre为面积并 } return 0; }