http://acm.hdu.edu.cn/showproblem.php?pid=1542
题意:给n个矩形的左下角和右上角的顶点坐标,求n个矩形的面积并。
思路:
首先离散化,因为矩形端点坐标非常大。如果从下向上扫描,离散横坐标,如果从右向左扫描,离散纵坐标。这里,离散横坐标,从下向上扫描。
离散时,先对横坐标排序,去重,最后每个下标对应一个横坐标。
顺序扫描seg[]中的边,每扫描一条边,先找到该边对应离散后的左右端点(l,r)然后更新线段[l,r],如果该边是下边,那么对应的线段树区间+1,否则-1。若区间cnt > 0 ,说明该区间完全被覆盖,其长度可以直接算出。若区间cnt == 0,说明没有完全覆盖,由其左右儿子的区间覆盖长度和算出,区间cnt 不会出现负值,因为每次扫描都是从矩形的下边开始,扫描下边的时候cnt+1。每扫描一条边seg[i],都要计算加上或删除这条边后(即更新后)总区间被覆盖的长度,用该长度乘seg[i+1].h-seg[i].h。最后累加。
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int maxn = 120; struct node { double l,r,h; int f; bool operator < (const struct node & tmp) const { return h < tmp.h;//按h从小到大排序 } }seg[maxn*2]; struct line { int l,r;//线段左右端点 int cnt;//该节点被覆盖的情况。正值完全覆盖,0不完全覆盖 double len;//该节点被覆盖的长度 }tree[maxn*8]; double x[2*maxn]; void build(int v, int l, int r) { tree[v].l = l; tree[v].r = r; tree[v].cnt = 0; tree[v].len = 0; if(l == r) return; int mid = (l+r)>>1; build(v*2,l,mid); build(v*2+1,mid+1,r); } int binsearch(double key,int k)//二分查找横坐标对应离散后的编号 { int low = 1; int high = k; while(low <= high) { int mid = (low+high)>>1; if(x[mid] == key) return mid; if(key < x[mid]) high = mid-1; else low = mid+1; } return -1; } void getlen(int v) { if(tree[v].cnt)//该节点完全覆盖, { tree[v].len = x[ tree[v].r+1 ] - x[ tree[v].l ]; return; } if(tree[v].l == tree[v].r)//不是一条线段 { tree[v].len = 0; return; } tree[v].len = tree[v*2].len + tree[v*2+1].len; } void update(int v, int l, int r, int f) { if(tree[v].l == l && tree[v].r == r) { tree[v].cnt += f; getlen(v); return; } int mid = (tree[v].l + tree[v].r)>>1; if(r <= mid) update(v*2,l,r,f); else if(l > mid) update(v*2+1,l,r,f); else { update(v*2,l,mid,f); update(v*2+1,mid+1,r,f); } getlen(v); } int main() { int n; double a,b,c,d; int item = 1; while(~scanf("%d",&n)&&n) { int num = 1; for(int i = 1; i <= n; i++) { scanf("%lf %lf %lf %lf",&a,&b,&c,&d); seg[num].l = a; seg[num].r = c; seg[num].h = b; seg[num].f = 1; x[num++] = a; seg[num].l = a; seg[num].r = c; seg[num].h = d; seg[num].f = -1; x[num++] = c; } sort(seg+1,seg+num); sort(x+1,x+num); int k = 1; for(int i = 2; i < num; i++) { if(x[i] != x[i-1]) //去重 x[++k] = x[i]; } build(1,1,k); double ans = 0; for(int i = 1; i < num; i++) { int l = binsearch(seg[i].l,k); int r = binsearch(seg[i].r,k)-1; update(1,l,r,seg[i].f); ans += (seg[i+1].h - seg[i].h)*tree[1].len; } printf("Test case #%d\n",item++); printf("Total explored area: %.2lf\n\n",ans); } return 0; }