//永远只考虑根节点的信息,说明在query时不会调用pushdown //所有操作均是成对出现,且先加后减 // #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N = 100010; int n; //存每一个操作 struct Segment { //区间长度 double x; //纵坐标 double y1, y2; //权值 int k; //按横坐标排序 bool operator< (const Segment &t)const { return x < t.x; } }seg[N * 2]; struct Node { int l, r; //被覆盖的次数是多少 int cnt; //所有权值大于0的长度和是多少 //不考虑祖先节点cnt的前提下(永远只向下看),cnt>0的区间总长 double len; }tr[N * 8]; //离散化,y vector<double> ys; //找离散化之后的值 int find(double y) { return lower_bound(ys.begin(), ys.end(), y) - ys.begin(); } void pushup(int u) { if (tr[u].cnt) // r+1才是下标 ,减完之后是长度 tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l]; //如果不是叶节点,再用两个儿子算 else if (tr[u].l != tr[u].r) { tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len; } //如果是叶节点 else tr[u].len = 0; } void build(int u, int l, int r) { // cnt是0,len是0 tr[u] = {l, r, 0, 0}; if (l != r) { int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); } } void modify(int u, int l, int r, int k) { // if (tr[u].l >= l && tr[u].r <= r) { tr[u].cnt += k; pushup(u); } else { int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify(u << 1, l, r, k); if (r > mid) modify(u << 1 | 1, l, r, k); pushup(u); } } int main() { int T = 1; while (scanf("%d", &n), n) { ys.clear(); for (int i = 0, j = 0; i < n; i ++ ) { double x1, y1, x2, y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); //对应的区间加1 seg[j ++ ] = {x1, y1, y2, 1}; //对应的区间减1 seg[j ++ ] = {x2, y1, y2, -1}; ys.push_back(y1), ys.push_back(y2); } //对应区间 //离散化 sort(ys.begin(), ys.end()); ys.erase(unique(ys.begin(), ys.end()), ys.end()); //从1号点开始,存的是区间,所以是0到ys.size()-2 build(1, 0, ys.size() - 2); //排序 sort(seg, seg + n * 2); double res = 0; for (int i = 0; i < n * 2; i ++ ) { //第一条线不能加 ,0的时候,还没点加进去 if (i > 0) // 为1的长度 区间长度 res += tr[1].len * (seg[i].x - seg[i - 1].x); //要把当前的操作 加 到线段树里去 // 左区间 右区间 权值 modify(1, find(seg[i].y1), find(seg[i].y2) - 1, seg[i].k); } printf("Test case #%d ", T ++ ); printf("Total explored area: %.2lf ", res); } return 0; }