/*
做这题的时候是参考别人的程序
现在拿出来回顾下
好好理解一下这题的思路
第一,这题需要离散化
第二,计算矩形并的面积 当某一条线段被覆盖两次或两次以上 计算一次面积 具体的画个图比较明了
第三,也是最重要的,线段数once, more的更新, 当时没有掌握到线段数的精髓 也是迷迷糊糊的,
现在回来想想,once more 是第一类信息 表示当前区间的性质
所有递归回来的时候 由递推关系更新once more 而more 又是由once 推出来的
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define EPS 1e-8
#define L(x) ((x) << 1)
#define R(x) ((x) << 1 | 1)
const int MAXN = 2222;
struct Line
{
double x,y1,y2;
int flag;
};
bool equal(double a, double b){
if( fabs(a - b) <= EPS)
return true;
return false;
}
int doubleCmp(const void *a, const void *b){
return *(double*)a - *(double*)b > 0 ? 1 : -1;
}
int cmp(const void *a, const void *b){
Line *l1 = (Line*)a;
Line *l2 = (Line*)b;
if( equal(l1 -> x, l2 -> x)){
return l1 -> flag - l2 -> flag;
}
return (l1 -> x) - (l2 -> x) > 0 ? 1 : -1; //浮点数比较大小 要注意
}
struct segTree
{
int l,r;
int cov; //覆盖次数
double once, more; //覆盖一次的长度和覆盖两次的长度
};
Line line[2 * MAXN];
segTree tree[MAXN * 4]; //空间
double pointy[2 * MAXN];
void bulid(int left, int right, int t){
tree[t].l = left;
tree[t].r = right;
tree[t].cov = 0;
tree[t].once = tree[t].more = 0.0;
tree[L(t)].cov = 0;
tree[L(t)].once = tree[L(t)].more = 0.0;
tree[R(t)].cov = 0;
tree[R(t)].once = tree[R(t)].more = 0.0;
if( right - left == 1)
return;
int mid = (left + right) >> 1;
bulid(left, mid, L(t));
bulid(mid, right, R(t));
}
int bsearch(int l, int r, double temp){
while(l <= r){
int mid = ( l + r ) >> 1;
if( equal(pointy[mid], temp)){
return mid;
} else if( pointy[mid] > temp ){
r = mid - 1;
} else if(pointy[mid] < temp){
l = mid + 1;
}
}
return -1;
}
void rr(int t,int l, int r){
//分情况讨论
if(tree[t].cov > 1){ //覆盖多次
tree[t].once = 0;
tree[t].more = pointy[r] - pointy[l];
} else if( tree[t].cov == 1){ //覆盖一次 则覆盖两次的长度为儿子覆盖一次和两次的长度之和 覆盖一次的长度则为总长度减去覆盖多次的长度 因为不可能存在没被覆盖的区域
tree[t].more = tree[L(t)].more + tree[R(t)].more
+ tree[L(t)].once + tree[R(t)].once;
tree[t].once = pointy[r] - pointy[l] - tree[t].more;
} else { // == 0 没被覆盖
if( r - l == 1){ //叶子
tree[t].once = tree[t].more = 0;
} else { //由左右孩子的once more 更新自己的once more
tree[t].once = tree[L(t)].once + tree[R(t)].once;
tree[t].more = tree[L(t)].more + tree[R(t)].more;
}
}
}
void update(int l, int r, int t, int flag){
/*这递归不是我的风格 哈哈 真是copy啊*/
if( l >= tree[t].r || r <= tree[t].l) //不在这个区间
return;
if( l <= tree[t].l && r >= tree[t].r){ //包含这个区间
tree[t].cov += flag;
rr(t,tree[t].l,tree[t].r); //如果覆盖区间 则rr once more 然后直接返回
return; //important
}
update(l, r, L(t), flag);
update(l, r, R(t), flag);
rr(t,tree[t].l,tree[t].r); //rr函数更新节点的once more
}
int main(){
int t, n, i, j;
double x1, x2, y1, y2;
scanf("%d",&t);
while( t-- ){
scanf("%d",&n);
j = 1;
for(i = 1; i <= n; i++){
scanf("%lf%lf%lf%lf",&x1, &y1, &x2, &y2);
line[j].x = x1;
line[j].y1 = y1;
line[j].y2 = y2;
line[j].flag = 1;
pointy[j] = y1;
j++;
line[j].x = x2;
line[j].y1 = y1;
line[j].y2 = y2;
line[j].flag = -1;
pointy[j] = y2;
j++;
}
qsort(pointy + 1, 2 * n , sizeof(pointy[0]), doubleCmp);
qsort(line + 1, 2 * n , sizeof(line[0]), cmp);
//离散化
int size = 1;
for(i = 2; i <= 2 * n; i++){
if(!equal(pointy[i] , pointy[i - 1])){
pointy[++size] = pointy[i];
}
}
bulid(1, size, 1);
double ans = 0.0;
for(i = 1; i <= 2 * n; i++){
if(i != 1){
ans += (line[i].x - line[i - 1].x) * tree[1].more;
}
int y1 = bsearch(1, size, line[i].y1);
int y2 = bsearch(1, size, line[i].y2);
update(y1, y2, 1, line[i].flag);
}
printf("%.2lf\n",ans);
}
return 0;
}