线段树:
1. 永远只考虑根节点的信息 -> 说明query时不需要pushdown
2. 所有操作都是成对出现, 且先加后减(要么区间加1, 要么区间减1, 且区间是一致的) -> modify时不需要pushdown
3. 离散化, y1 表示的是一个区间, y1表示y1~y2, y2表示y2~y3 … , 如果要求L~R区间, 就是求yL ~ yR - 1
第3点解释: 我们需要的是求区间长度, 如果维护的是一个点的话, 是没有长度意义的, 总共有2n个点, 所以维护的是2n-1个区间
import java.io.*;
import java.util.*;
class Main{
static BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 100010, n;
static Segment[] seg;
static Node[] tr = new Node[N * 8]; //映射到y坐标的线段树
static List<Double> ys = new ArrayList<>(); //加入ys, 实现y坐标的离散化
public static void main(String[] args) throws Exception{
int T = 1;
while (true){
n = Integer.valueOf(read.readLine());
seg = new Segment[n * 2];
if(n == 0) break;
for(int i = 0, j = 0; i < n; i++){
String[] ss = read.readLine().split(" ");
double x1 = Double.valueOf(ss[0]), y1 = Double.valueOf(ss[1]);
double x2 = Double.valueOf(ss[2]), y2 = Double.valueOf(ss[3]);
seg[j++] = new Segment(x1, y1, y2, 1);
seg[j++] = new Segment(x2, y1, y2, -1);
ys.add(y1); ys.add(y2);
}
//排序, 去重 实现离散化
Collections.sort(ys);
ys = unique(ys);
//构建线段树
build(1, 0, ys.size() - 2);
Arrays.sort(seg, (o1, o2) -> o1.x > o2.x ? 1 : -1);
double res = 0;
for(int i = 0; i < n * 2; i++){
if(i > 0) 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);
}
log.write("Test case #" + T++ + "\n");
log.write("Total explored area: " + String.format("%.2f", res) + "\n");
log.write("\n");
}
log.flush();
}
public static void pushUp(int u){
//如果cnt > 0, 则整个区间长度就是len
if(tr[u].cnt > 0){
//tr[u].r,是区间从r到r + 1的左端点, 所以tr[u].r + 1 -> 右端点, 因为存的是区间
tr[u].len = ys.get(tr[u].r + 1) - ys.get(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].l == tr[u].r 叶节点的话, 区间长度为0
tr[u].len = 0;
}
}
}
public static void build(int u, int l, int r){
tr[u] = new Node(l, r, 0, 0);
if(l != r){
int mid = l + r >> 1;
build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
}
//这里不需要pushup是因为cnt和len都是0, pushup后结果一样
}
public static 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);
}
}
public static int find(double target){
int l = 0, r = ys.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(ys.get(mid) >= target) r = mid;
else l = mid + 1;
}
return l;
}
public static List<Double> unique(List<Double> list){
List<Double> res = new ArrayList(list.size());
for(int i = 0; i < list.size(); i++){
if(res.isEmpty() || res.get(res.size() - 1) != list.get(i)) res.add(list.get(i));
}
return res;
}
static class Segment{
double x, y1, y2;
int k;
Segment(double x, double y1, double y2, int k){
this.x = x; this.y1 = y1; this.y2 = y2; this.k = k;
}
}
static class Node{
int l, r;
int cnt; //当前区间整个被覆盖的次数
double len; //不考虑祖先节点cnt的前提下, cnt > 0的区间总长
Node(int l, int r, int cnt, double len){
this.l = l; this.r = r; this.cnt = cnt; this.len = len;
}
}
}